From effc12fbd6ffd4a388f4aa31425ba8a9b33faa82 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 31 Jul 2023 06:03:39 +0300 Subject: Paginate the resource data items. --- gn3/auth/authorisation/resources/models.py | 62 ++++++++++++++++++++++-------- gn3/auth/authorisation/resources/views.py | 35 ++++++++++++++++- 2 files changed, 80 insertions(+), 17 deletions(-) (limited to 'gn3/auth/authorisation') 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//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(): -- cgit v1.2.3