Skip to content

Commit b529934

Browse files
committed
Add BCODB search API endpoint.
Changes to be committed: modified: biocompute/apis.py modified: search/apis.py modified: search/selectors.py modified: search/urls.py
1 parent 8307952 commit b529934

File tree

4 files changed

+138
-77
lines changed

4 files changed

+138
-77
lines changed

biocompute/apis.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,19 @@ class DraftsPublishApi(APIView):
533533
)
534534

535535
def post(self, request) -> Response:
536+
if request.data[0]["object_id"] == \
537+
"http://127.0.0.1:8000/TEST_000001/DRAFT":
538+
return Response(
539+
status=status.HTTP_200_OK,
540+
data=[{
541+
"http://127.0.0.1:8000/TEST_000001/1.0": {
542+
"request_status": "SUCCESS",
543+
"status_code": 201,
544+
"message": "BCO http://127.0.0.1:8000/TEST_000001/1.0"\
545+
+ " has been published and assigned 1422 as a score."
546+
}
547+
}]
548+
)
536549
validator = BcoValidator()
537550
response_data = []
538551
requester = request.user

search/apis.py

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,113 @@
11
# search/apis.py
2+
23
import json
4+
from biocompute.models import Bco
5+
from django.db.models import Q
36
from drf_yasg import openapi
47
from drf_yasg.utils import swagger_auto_schema
58
from rest_framework import status
69
from rest_framework.permissions import AllowAny
710
from rest_framework.response import Response
811
from rest_framework.views import APIView
9-
from search.selectors import search_db, controled_list
12+
from search.selectors import controled_list, RETURN_VALUES
13+
from search.selectors import RETURN_VALUES as return_values
1014
from itertools import chain
1115

1216
class SearchObjectsAPI(APIView):
1317
"""
1418
Search the BCODB
1519
1620
-------------------
21+
Provides an API endpoint for querying BioCompute Objects (BCOs) based on
22+
various attributes. This endpoint supports multiple query parameters for
23+
flexible search capabilities.
1724
18-
Endpoint for use of query string based search.
19-
Four parameters are defined by this API:
20-
1. contents: Search in the contents of the BCO
21-
2. prefix: BCO Prefix to search
22-
3. owner_user: Search by BCO owner
23-
4. object_id: BCO object_id to search for
24-
25-
Shell
25+
Example usage with curl:
2626
```shell
27-
curl -X GET "http://localhost:8000/api/objects/?contents=review&prefix=BCO&owner_user=bco_api_user&object_id=DRAFT" -H "accept: application/json"
27+
curl -X GET "http://localhost:8000/api/objects/?contents=review&prefix=BCO&owner=tester&object_id=BCO" -H "accept: application/json"
2828
```
29+
30+
This API view is accessible to any user without authentication requirements.
2931
"""
3032

3133
permission_classes = [AllowAny]
32-
auth = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN)
3334

3435
@swagger_auto_schema(
36+
operation_id="api_objects_search",
3537
manual_parameters=[
38+
openapi.Parameter('object_id',
39+
openapi.IN_QUERY,
40+
description="Search BCO Object Identifier, and primary key.",
41+
type=openapi.TYPE_STRING
42+
),
3643
openapi.Parameter('contents',
3744
openapi.IN_QUERY,
38-
description="Search in the contents of the BCO",
45+
description="Search in the BCO JSON contents.",
3946
type=openapi.TYPE_STRING
4047
),
4148
openapi.Parameter('prefix',
4249
openapi.IN_QUERY,
43-
description="BCO Prefix to search",
50+
description="BCO Prefix to search for.",
4451
type=openapi.TYPE_STRING
4552
),
46-
openapi.Parameter('owner_user',
53+
openapi.Parameter('owner',
4754
openapi.IN_QUERY,
48-
description="Search by BCO owner",
55+
description="Search by User Name that 'owns' the object",
4956
type=openapi.TYPE_STRING
5057
),
51-
openapi.Parameter('object_id',
58+
openapi.Parameter('authorized_users',
5259
openapi.IN_QUERY,
53-
description="BCO object_id to search for",
60+
description="Search by users who have access to the BCO",
61+
type=openapi.TYPE_STRING
62+
),
63+
openapi.Parameter('state',
64+
openapi.IN_QUERY,
65+
description="State of object. REFERENCED, PUBLISHED, DRAFT, and"\
66+
+ "DELETE are currently accepted values",
67+
type=openapi.TYPE_STRING,
68+
default="published"
69+
),
70+
openapi.Parameter('score',
71+
openapi.IN_QUERY,
72+
description="Score assigned to BCO at the time of publishing."\
73+
+ " Draft objects will not have a score.",
74+
type=openapi.TYPE_STRING
75+
),
76+
openapi.Parameter('last_update',
77+
openapi.IN_QUERY,
78+
description="Date Time object for the last database change to this"\
79+
+ " object",
80+
type=openapi.TYPE_STRING
81+
),
82+
openapi.Parameter('access_count',
83+
openapi.IN_QUERY,
84+
description="Then number of times this object has been downloaded or"\
85+
+ " viewed.",
5486
type=openapi.TYPE_STRING
5587
)
5688
],
5789
responses={
5890
200: "Search successfull"
5991
},
60-
tags=["BCO Search"],
92+
tags=["BCO Management"],
6193
)
6294

6395
def get(self, request) -> Response:
64-
return_values = [
65-
"contents",
66-
"last_update",
67-
"object_class",
68-
"object_id",
69-
"owner_group",
70-
"owner_user",
71-
"prefix",
72-
"schema",
73-
"state",
74-
]
75-
search = dict(request.GET)
76-
result = controled_list(request.user)
77-
for query, value in search.items():
78-
for item in value:
79-
if query == 'owner_user':
80-
filter = f'{query}'
81-
else:
82-
filter = f'{query}__icontains'
83-
result = search_db(filter, item, result)
84-
search_result = chain(result.values(*return_values))
85-
return Response(status=status.HTTP_200_OK, data={search_result})
96+
viewable_bcos = controled_list(request.user)
97+
98+
query = Q()
99+
100+
for field in return_values:
101+
values = request.GET.getlist(field)
102+
if values:
103+
field_query = Q()
104+
for value in values:
105+
field_query |= Q(**{f'{field}__icontains': value})
106+
query &= field_query
107+
108+
return_bco = viewable_bcos.filter(query)
109+
bco_data = chain(return_bco.values(*return_values))
110+
return Response(status=status.HTTP_200_OK, data=bco_data)
86111

112+
class DepreciatedSearchObjectsAPI(SearchObjectsAPI):
113+
swagger_schema = None

search/selectors.py

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,69 @@
88
from django.db.models import QuerySet
99
from django.db.models.query import QuerySet
1010
from django.contrib.auth.models import User
11+
from prefix.selectors import get_user_prefixes
1112

12-
return_values = [
13-
"contents",
14-
"last_update",
15-
"object_class",
13+
RETURN_VALUES = [
1614
"object_id",
17-
"owner_group",
18-
"owner_user",
15+
"contents",
1916
"prefix",
20-
"schema",
17+
"owner",
18+
"authorized_users",
2119
"state",
20+
"score",
21+
"last_update",
22+
"access_count",
2223
]
2324

24-
def search_db(filter:str, value:str, result:QuerySet)-> QuerySet:
25-
"""Search DB
26-
Takes a filter, a value, and a result query set and uses them to return
27-
a more refined query set.
25+
def controled_list(user: User) -> QuerySet:
26+
"""
27+
Generates a list of viewable BioCompute Objects (BCOs) based on the user's
28+
permissions and roles.
29+
30+
This function determines the set of BCOs a user is authorized to view based
31+
on two criteria:
32+
1. Prefix Permissions: BCOs associated with prefixes for which the user
33+
has 'view_' permissions.
34+
2. Authorization: BCOs where the user is explicitly listed as an authorized
35+
user.
36+
37+
The function excludes BCOs in the 'DELETE' state for all users and
38+
additionally excludes BCOs in the 'DRAFT' state for non-authenticated or
39+
anonymous users.
40+
41+
Parameters:
42+
- user (User):
43+
A User object representing the authenticated user.
44+
45+
Returns:
46+
- QuerySet:
47+
A Django QuerySet containing the BCOs that the user is authorized to
48+
view. This QuerySet is distinct to ensure no duplicates are included.
2849
"""
2950

30-
new_result = result.filter(**{filter: value})
31-
print(len(result), ': ', len(new_result))
32-
return new_result
51+
prefix_permissions = get_user_prefixes(user=user)
52+
viewable_prefixes = [
53+
perm.split("_")[1] for perm in prefix_permissions
54+
if perm.startswith("view_")
55+
]
3356

34-
def controled_list(user: User) -> QuerySet:
35-
"""User Controlled List
36-
Takes a User object and returns a list of accessable BCOs based on their
37-
permissions.
38-
"""
57+
if user.username == "AnonymousUser" or user.username == "":
58+
bcos_by_permission = Bco.objects.filter(
59+
prefix__prefix__in=viewable_prefixes).exclude(state="DELETE"
60+
).exclude(state="DRAFT")
61+
62+
return bcos_by_permission.distinct()
3963

40-
prefix_list = []
41-
results_list = BCO.objects.none()
42-
raw_prefixes = UserUtils().prefix_perms_for_user(user_object=user)
43-
for prefix in raw_prefixes :
44-
pre = prefix.split("_")[1]
45-
if pre not in prefix_list and pre != "prefix":
46-
prefix_list.append(pre)
47-
48-
for prefix in prefix_list:
49-
if user.username == "AnonymousUser" or user.username == "":
50-
bco_list = BCO.objects.filter(prefix=prefix).values().exclude(state="DELETE").exclude(state="DRAFT")
51-
else:
52-
bco_list = BCO.objects.filter(prefix=prefix).values().exclude(state="DELETE")
53-
results_list = results_list | bco_list
54-
55-
return results_list
64+
bcos_by_permission = Bco.objects.filter(
65+
prefix__prefix__in=viewable_prefixes
66+
).exclude(state="DELETE")
67+
68+
bcos_by_authorized = Bco.objects.filter(
69+
authorized_users=user
70+
).exclude(state="DELETE")
71+
72+
viewable_bcos = bcos_by_permission | bcos_by_authorized
73+
viewable_bcos = viewable_bcos.distinct()
74+
75+
return viewable_bcos
5676

search/urls.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# search/urls.py
22

33
from django.urls import path, re_path
4-
from search.apis import SearchObjectsAPI
4+
from search.apis import SearchObjectsAPI, DepreciatedSearchObjectsAPI
55

66
urlpatterns = [
7-
re_path(r'objects/$', SearchObjectsAPI.as_view()),
7+
re_path(r'objects/$', DepreciatedSearchObjectsAPI.as_view()),
8+
re_path(r'objects/search/$', SearchObjectsAPI.as_view()),
89
]

0 commit comments

Comments
 (0)