about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-07-31 06:03:39 +0300
committerFrederick Muriuki Muriithi2023-07-31 06:03:39 +0300
commiteffc12fbd6ffd4a388f4aa31425ba8a9b33faa82 (patch)
tree4416b6fc10380840cc1b93c76c3b1d6b6477b0e8
parent58f4b3dfd10d6166f4fc58071bdb8f1febb06392 (diff)
downloadgenenetwork3-effc12fbd6ffd4a388f4aa31425ba8a9b33faa82.tar.gz
Paginate the resource data items.
-rw-r--r--gn3/auth/authorisation/resources/models.py62
-rw-r--r--gn3/auth/authorisation/resources/views.py35
2 files changed, 80 insertions, 17 deletions
diff --git a/gn3/auth/authorisation/resources/models.py b/gn3/auth/authorisation/resources/models.py
index 5ff5983..b634a35 100644
--- a/gn3/auth/authorisation/resources/models.py
+++ b/gn3/auth/authorisation/resources/models.py
@@ -3,7 +3,7 @@ import json
 import sqlite3
 from uuid import UUID, uuid4
 from functools import reduce, partial
-from typing import Any, Dict, Sequence, NamedTuple
+from typing import Any, Dict, Sequence, Optional, NamedTuple
 
 from gn3.auth import db
 from gn3.auth.dictify import dictify
@@ -201,6 +201,22 @@ def user_resources(conn: db.DbConnection, user: User) -> Sequence[Resource]:
         return user_group(conn, user).map(__all_resources__).maybe(# type: ignore[arg-type,misc]
             public_resources(conn), lambda res: res)# type: ignore[arg-type,return-value]
 
+def resource_data(conn, resource, offset: int = 0, limit: Optional[int] = None) -> tuple[dict, ...]:
+    """
+    Retrieve the data for `resource`, optionally limiting the number of items.
+    """
+    resource_data_function = {
+        "mrna": mrna_resource_data,
+        "genotype": genotype_resource_data,
+        "phenotype": phenotype_resource_data
+    }
+    with db.cursor(conn) as cursor:
+        return tuple(
+            dict(data_row) for data_row in
+            resource_data_function[
+                resource.resource_category.resource_category_key](
+                    cursor, resource.resource_id, offset, limit))
+
 def attach_resource_data(cursor: db.DbCursor, resource: Resource) -> Resource:
     """Attach the linked data to the resource"""
     resource_data_function = {
@@ -217,35 +233,49 @@ def attach_resource_data(cursor: db.DbCursor, resource: Resource) -> Resource:
         resource.group, resource.resource_id, resource.resource_name,
         resource.resource_category, resource.public, data_rows)
 
-def mrna_resource_data(
-        cursor: db.DbCursor, resource_id: UUID) -> Sequence[sqlite3.Row]:
+def mrna_resource_data(cursor: db.DbCursor,
+                       resource_id: UUID,
+                       offset: int = 0,
+                       limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
     """Fetch data linked to a mRNA resource"""
     cursor.execute(
-        "SELECT * FROM mrna_resources AS mr INNER JOIN linked_mrna_data AS lmr"
-        " ON mr.data_link_id=lmr.data_link_id "
-        "WHERE mr.resource_id=?",
+        (("SELECT * FROM mrna_resources AS mr "
+          "INNER JOIN linked_mrna_data AS lmr "
+          "ON mr.data_link_id=lmr.data_link_id "
+          "WHERE mr.resource_id=?") + (
+              f" LIMIT {limit}" if bool(limit) else "") (
+                  f" LIMIT {limit} OFFSET {offset}" if bool(limit) else "")),
         (str(resource_id),))
     return cursor.fetchall()
 
 def genotype_resource_data(
-        cursor: db.DbCursor, resource_id: UUID) -> Sequence[sqlite3.Row]:
+        cursor: db.DbCursor,
+        resource_id: 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=?",
+        (("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 phenotype_resource_data(
-        cursor: db.DbCursor, resource_id: UUID) -> Sequence[sqlite3.Row]:
+        cursor: db.DbCursor,
+        resource_id: UUID,
+        offset: int = 0,
+        limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
     """Fetch data linked to a Phenotype resource"""
+    from gn3.debug import __pk__
     cursor.execute(
-        "SELECT * FROM phenotype_resources AS pr "
-        "INNER JOIN linked_phenotype_data AS lpd "
-        "ON pr.data_link_id=lpd.data_link_id "
-        "WHERE pr.resource_id=?",
+        ("SELECT * FROM phenotype_resources AS pr "
+         "INNER JOIN linked_phenotype_data AS lpd "
+         "ON pr.data_link_id=lpd.data_link_id "
+         "WHERE pr.resource_id=?") + (
+             f" LIMIT {limit} OFFSET {offset}" if bool(limit) else ""),
         (str(resource_id),))
     return cursor.fetchall()
 
diff --git a/gn3/auth/authorisation/resources/views.py b/gn3/auth/authorisation/resources/views.py
index 396a57e..2e6c244 100644
--- a/gn3/auth/authorisation/resources/views.py
+++ b/gn3/auth/authorisation/resources/views.py
@@ -10,7 +10,7 @@ from gn3.auth.db_utils import with_db_connection
 
 from .checks import authorised_for
 from .models import (
-    Resource, save_resource, resource_by_id, resource_categories,
+    Resource, save_resource, resource_data, resource_by_id, resource_categories,
     assign_resource_user, link_data_to_resource, unassign_resource_user,
     resource_category_by_id, unlink_data_from_resource,
     create_resource as _create_resource)
@@ -72,6 +72,39 @@ def view_resource(resource_id: uuid.UUID) -> Response:
             return jsonify(dictify(resource_by_id(
                 conn, the_token.user, resource_id)))
 
+def __safe_get_requests_page__(key: str = "page") -> int:
+    """Get the results page if it exists or default to the first page."""
+    try:
+        return abs(int(request.args.get(key, "1"), base=10))
+    except ValueError as _valerr:
+        return 1
+
+def __safe_get_requests_count__(key: str = "count_per_page") -> int:
+    """Get the results page if it exists or default to the first page."""
+    try:
+        count = request.args.get(key)
+        if count is None:
+            return None
+        return abs(int(count, base=10))
+    except ValueError as _valerr:
+        return None
+
+@resources.route("/view/<uuid:resource_id>/data")
+@require_oauth("profile group resource")
+def view_resource_data(resource_id: uuid.UUID) -> Response:
+    """Retrieve a particular resource's data."""
+    with require_oauth.acquire("profile group resource") as the_token:
+        db_uri = app.config["AUTH_DB"]
+        count_per_page = __safe_get_requests_count__("count_per_page")
+        page = (__safe_get_requests_page__("page") - 1)
+        with db.connection(db_uri) as conn:
+            resource = resource_by_id(conn, the_token.user, resource_id)
+            return jsonify(resource_data(
+                conn,
+                resource,
+                ((page * count_per_page) if bool(count_per_page) else page),
+                count_per_page))
+
 @resources.route("/data/link", methods=["POST"])
 @require_oauth("profile group resource")
 def link_data():