From e636acc412fef6b1d5aa08ac701bbadc3be52b80 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 8 Apr 2023 08:26:50 +0300 Subject: auth: Link genotype datasets to groups. --- gn3/auth/authorisation/data/genotypes.py | 45 +++++++++++++++++++++++++++++--- gn3/auth/authorisation/data/views.py | 34 +++++++++++++++++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/gn3/auth/authorisation/data/genotypes.py b/gn3/auth/authorisation/data/genotypes.py index 690293c..fe06726 100644 --- a/gn3/auth/authorisation/data/genotypes.py +++ b/gn3/auth/authorisation/data/genotypes.py @@ -1,11 +1,14 @@ """Handle linking of Genotype data to the Auth(entic|oris)ation system.""" +import uuid from typing import 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_genotype_data(conn: authdb.DbConnection) -> Iterable[dict]: """Retrive genotype data that is linked to user groups.""" @@ -39,11 +42,11 @@ def ungrouped_genotype_data( query = query + "WHERE " if len(params) > 0: - paramstr = ", ".join(["(?, ?, ?)"] * len(params)) + paramstr = ", ".join(["(%s, %s, %s)"] * len(params)) query = query + ( - "(s.SpeciesId, iset.InbredSetId, GenoFreezeId) " + "(s.SpeciesId, iset.InbredSetId, gf.Id) " f"NOT IN ({paramstr}) " - "AND ") + ) + ("AND " if bool(search_query) else "") if bool(search_query): query = query + ( @@ -51,7 +54,41 @@ def ungrouped_genotype_data( params = params + ((search_query,),)# type: ignore[operator] query = query + f"LIMIT {int(limit)} OFFSET {int(offset)}" + final_params = tuple(item for sublist in params for item in sublist) with gn3conn.cursor(DictCursor) as cursor: cursor.execute( - query, tuple(item for sublist in params for item in sublist)) + query, final_params) return tuple(row for row in 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_genotype_data( + conn: authdb.DbConnection, group: Group, datasets: dict) -> dict: + """Link genotye `datasets` to `group`.""" + with authdb.cursor(conn) as cursor: + cursor.executemany( + "INSERT INTO linked_genotype_data VALUES " + "(:data_link_id, :group_id, :SpeciesId, :InbredSetId, " + ":GenoFreezeId, :dataset_name, :dataset_fullname, " + ":dataset_shortname) " + "ON CONFLICT (SpeciesId, InbredSetId, GenoFreezeId) DO NOTHING", + tuple({ + "data_link_id": str(uuid.uuid4()), + "group_id": str(group.group_id), + **{ + key: value for key,value in dataset.items() if key in ( + "GenoFreezeId", "InbredSetId", "SpeciesId", + "dataset_fullname", "dataset_name", "dataset_shortname") + } + } for dataset in datasets)) + return { + "description": ( + f"Successfully linked {len(datasets)} to group " + f"'{group.group_name}'."), + "group": dictify(group), + "datasets": datasets + } diff --git a/gn3/auth/authorisation/data/views.py b/gn3/auth/authorisation/data/views.py index 70b14d7..55a7373 100644 --- a/gn3/auth/authorisation/data/views.py +++ b/gn3/auth/authorisation/data/views.py @@ -6,7 +6,7 @@ import random import string import datetime from functools import reduce, partial -from typing import Sequence, Iterable +from typing import Any, Sequence, Iterable import redis from MySQLdb.cursors import DictCursor @@ -21,14 +21,14 @@ from gn3.auth import db from gn3.auth.dictify import dictify from gn3.auth.db_utils import with_db_connection -from gn3.auth.authorisation.errors import NotFoundError +from gn3.auth.authorisation.errors import InvalidData, NotFoundError from gn3.auth.authorisation.roles.models import( revoke_user_role_by_name, assign_user_role_by_name) from gn3.auth.authorisation.groups.data import retrieve_ungrouped_data from gn3.auth.authorisation.groups.models import ( - Group, user_group, add_user_to_group) + Group, user_group, group_by_id, add_user_to_group) from gn3.auth.authorisation.resources.checks import authorised_for from gn3.auth.authorisation.resources.models import ( @@ -40,7 +40,8 @@ from gn3.auth.authorisation.errors import ForbiddenAccess from gn3.auth.authentication.oauth2.resource_server import require_oauth from gn3.auth.authentication.users import User, user_by_email, set_user_password -from gn3.auth.authorisation.data.genotypes import ungrouped_genotype_data +from gn3.auth.authorisation.data.genotypes import ( + link_genotype_data, ungrouped_genotype_data) data = Blueprint("data", __name__) @@ -336,3 +337,28 @@ def search_unlinked_data(): "phenotype": __search_phenotypes__ } return search_fns[dataset_type]() + +@data.route("/link/genotype", methods=["POST"]) +def link_genotypes() -> Response: + """Link genotype data to group.""" + def __values__(form) -> dict[str, Any]: + 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_datasets")): + raise InvalidData("Expected at least one dataset to be provided.") + return { + "group_id": uuid.UUID(form.get("group_id")), + "datasets": form.get("selected_datasets") + } + + def __link__(conn: db.DbConnection, group_id: uuid.UUID, datasets: dict): + return link_genotype_data(conn, group_by_id(conn, group_id), datasets) + + return jsonify(with_db_connection( + partial(__link__, **__values__(request.json)))) -- cgit v1.2.3