about summary refs log tree commit diff
path: root/gn_auth/auth/authorisation/resources/genotypes
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2024-09-25 18:48:07 -0500
committerFrederick Muriuki Muriithi2024-09-25 18:48:33 -0500
commit1968503b6b01e8612d1d2aaedd7c1d80092ba2de (patch)
tree3a549e0c46e3f221420ac3aca9fcba7e5ea4c47a /gn_auth/auth/authorisation/resources/genotypes
parenta3f0e670635ad4319e241ada50587af75a0a2901 (diff)
downloadgn-auth-1968503b6b01e8612d1d2aaedd7c1d80092ba2de.tar.gz
Implement genotype resource creation via API with resource data
Create the resource, assign the resource-owner role and link the
resource's data in a single API call.
Diffstat (limited to 'gn_auth/auth/authorisation/resources/genotypes')
-rw-r--r--gn_auth/auth/authorisation/resources/genotypes/__init__.py1
-rw-r--r--gn_auth/auth/authorisation/resources/genotypes/models.py107
-rw-r--r--gn_auth/auth/authorisation/resources/genotypes/views.py63
3 files changed, 171 insertions, 0 deletions
diff --git a/gn_auth/auth/authorisation/resources/genotypes/__init__.py b/gn_auth/auth/authorisation/resources/genotypes/__init__.py
new file mode 100644
index 0000000..f401e28
--- /dev/null
+++ b/gn_auth/auth/authorisation/resources/genotypes/__init__.py
@@ -0,0 +1 @@
+"""Initialise a genotypes resources package."""
diff --git a/gn_auth/auth/authorisation/resources/genotypes/models.py b/gn_auth/auth/authorisation/resources/genotypes/models.py
new file mode 100644
index 0000000..c64922e
--- /dev/null
+++ b/gn_auth/auth/authorisation/resources/genotypes/models.py
@@ -0,0 +1,107 @@
+"""Genotype data resources functions and utilities."""
+import uuid
+from typing import Optional, Sequence
+
+import sqlite3
+
+import gn_auth.auth.db.sqlite3 as db
+from gn_auth.auth.authorisation.resources.base import Resource
+from gn_auth.auth.authorisation.resources.data import __attach_data__
+
+
+def resource_data(
+        cursor: db.DbCursor,
+        resource_id: uuid.UUID,
+        offset: int = 0,
+        limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
+    """Fetch data linked to a Genotype resource"""
+    cursor.execute(
+        (("SELECT * FROM genotype_resources AS gr "
+          "INNER JOIN linked_genotype_data AS lgd "
+          "ON gr.data_link_id=lgd.data_link_id "
+          "WHERE gr.resource_id=?") + (
+              f" LIMIT {limit} OFFSET {offset}" if bool(limit) else "")),
+        (str(resource_id),))
+    return cursor.fetchall()
+
+def link_data_to_resource(
+        conn: db.DbConnection,
+        resource: Resource,
+        data_link_id: uuid.UUID) -> dict:
+    """Link Genotype data with a resource using the GUI."""
+    with db.cursor(conn) as cursor:
+        params = {
+            "resource_id": str(resource.resource_id),
+            "data_link_id": str(data_link_id)
+        }
+        cursor.execute(
+            "INSERT INTO genotype_resources VALUES"
+            "(:resource_id, :data_link_id)",
+            params)
+        return params
+
+def unlink_data_from_resource(
+        conn: db.DbConnection,
+        resource: Resource,
+        data_link_id: uuid.UUID) -> dict:
+    """Unlink data from Genotype resources"""
+    with db.cursor(conn) as cursor:
+        cursor.execute("DELETE FROM genotype_resources "
+                       "WHERE resource_id=? AND data_link_id=?",
+                       (str(resource.resource_id), str(data_link_id)))
+        return {
+            "resource_id": str(resource.resource_id),
+            "dataset_type": resource.resource_category.resource_category_key,
+            "data_link_id": data_link_id
+        }
+
+def attach_resources_data(
+        cursor, resources: Sequence[Resource]) -> Sequence[Resource]:
+    """Attach linked data to Genotype resources"""
+    placeholders = ", ".join(["?"] * len(resources))
+    cursor.execute(
+        "SELECT * FROM genotype_resources AS gr "
+        "INNER JOIN linked_genotype_data AS lgd "
+        "ON gr.data_link_id=lgd.data_link_id "
+        f"WHERE gr.resource_id IN ({placeholders})",
+        tuple(str(resource.resource_id) for resource in resources))
+    return __attach_data__(cursor.fetchall(), resources)
+
+
+def insert_and_link_data_to_resource(
+        cursor,
+        resource_id: uuid.UUID,
+        species_id: int,
+        population_id: int,
+        dataset_id: int,
+        dataset_name: str,
+        dataset_fullname: str,
+        dataset_shortname: str
+) -> dict:
+    """Link the genotype identifier data to the genotype resource."""
+    params = {
+        "resource_id": str(resource_id),
+        "data_link_id": str(uuid.uuid4()),
+        "species_id": species_id,
+        "population_id": population_id,
+        "dataset_id": dataset_id,
+        "dataset_name": dataset_name,
+        "dataset_fullname": dataset_fullname,
+        "dataset_shortname": dataset_shortname
+    }
+    cursor.execute(
+        "INSERT INTO linked_genotype_data "
+        "VALUES ("
+        ":data_link_id,"
+        ":species_id,"
+        ":population_id,"
+        ":dataset_id,"
+        ":dataset_name,"
+        ":dataset_fullname,"
+        ":dataset_shortname,"
+        ")",
+        params)
+    cursor.execute(
+        "INSERT INTO genotype_resources VALUES (:resource_id, :data_link_id)",
+        params)
+    return params
diff --git a/gn_auth/auth/authorisation/resources/genotypes/views.py b/gn_auth/auth/authorisation/resources/genotypes/views.py
new file mode 100644
index 0000000..4908ff3
--- /dev/null
+++ b/gn_auth/auth/authorisation/resources/genotypes/views.py
@@ -0,0 +1,63 @@
+"""Genotype-resources-specific views."""
+from flask import jsonify, Blueprint
+from pymonad.either import Left, Right
+
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.authorisation.resources.request_utils import check_form
+from gn_auth.auth.authentication.oauth2.resource_server import require_oauth
+from gn_auth.auth.authorisation.resources.models import create_resource
+from gn_auth.auth.authorisation.resources.common import (
+    assign_resource_owner_role)
+
+
+from .models import insert_and_link_data_to_resource
+
+genobp = Blueprint("genotypes", __name__)
+
+@genobp.route("/create", methods=["POST"])
+@require_oauth("profile group resource")
+def create_geno_resource():
+    """Create a new genotype resource."""
+    with (require_oauth.acquire("profile group resource") as _token,
+          db.connection(app.config["AUTH_DB"]) as conn,
+          db.cursor(conn) as cursor):
+
+        return check_form(
+            request.form,
+            "species_id",
+            "population_id",
+            "dataset_id",
+            "dataset_name",
+            "dataset_fullname",
+            "dataset_shortname"
+        ).then(
+            lambda form: user_group(conn, _token.user).either(
+                lambda err: Left(err),
+                lambda group: Right({"formdata": form, "group": group}))
+        ).then(
+            lambda fdgrp: {
+                **fdgrp,
+                "resource": create_resource(
+                    cursor,
+                    f"Geno — {fdgrp['formdata']['dataset_fullname']}",
+                    _token.user,
+                    fdgrp["group"],
+                    fdgrp["formdata"].get("public", "on") // "on")}
+        ).then(
+            lambda fdgrpres: {
+                **fdgrpres,
+                "owner_role": assign_resource_owner_role(
+                    cursor,
+                    fdgrpres["resource"],
+                    _token.user)}
+        ).then(
+            lambda fdgrpres: insert_and_link_data_to_resource(
+                cursor,
+                fdgrpres["resource"].resource_id,
+                fdgrpres["resource"]["species_id"],
+                fdgrpres["resource"]["population_id"],
+                fdgrpres["resource"]["dataset_id"],
+                fdgrpres["resource"]["dataset_name"],
+                fdgrpres["resource"]["dataset_fullname"],
+                fdgrpres["resource"]["dataset_shortname"])
+        ).either(lambda error: (jsonify(error), 400), jsonify)