Skip to content

Commit

Permalink
feat: Add ability to get unassigned taxonomies (#33945)
Browse files Browse the repository at this point in the history
This adds a query param to fetch unassigned taxonomies, i.e. taxonomies
that do not belong to any org.
  • Loading branch information
yusuf-musleh authored Dec 18, 2023
1 parent 9a46bdf commit 9e14fa4
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
18 changes: 18 additions & 0 deletions openedx/core/djangoapps/content_tagging/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ def get_taxonomies_for_org(
)


def get_unassigned_taxonomies(enabled=True) -> QuerySet:
"""
Generate a list of the enabled orphaned Taxomonies, i.e. that do not belong to any
organization. We don't use `TaxonomyOrg.get_relationships` as that returns
Taxonomies which are available for all Organizations when no `org` is provided
"""
return oel_tagging.get_taxonomies(enabled=enabled).filter(
~(
Exists(
TaxonomyOrg.objects.filter(
taxonomy=OuterRef("pk"),
rel_type=TaxonomyOrg.RelType.OWNER,
)
)
)
)


def get_content_tags(
object_key: ContentKey,
taxonomy_id: int | None = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class TaxonomyOrgListQueryParamsSerializer(TaxonomyListQueryParamsSerializer):
queryset=Organization.objects.all(),
required=False,
)
unassigned: fields.Field = serializers.BooleanField(required=False)


class TaxonomyUpdateOrgBodySerializer(serializers.Serializer):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ def _test_list_taxonomy(
user_attr: str,
expected_taxonomies: list[str],
enabled_parameter: bool | None = None,
org_parameter: str | None = None
org_parameter: str | None = None,
unassigned_parameter: bool | None = None
) -> None:
"""
Helper function to call the list endpoint and check the response
Expand All @@ -288,7 +289,11 @@ def _test_list_taxonomy(
self.client.force_authenticate(user=user)

# Set parameters cleaning empty values
query_params = {k: v for k, v in {"enabled": enabled_parameter, "org": org_parameter}.items() if v is not None}
query_params = {k: v for k, v in {
"enabled": enabled_parameter,
"org": org_parameter,
"unassigned": unassigned_parameter,
}.items() if v is not None}

response = self.client.get(url, query_params, format="json")

Expand Down Expand Up @@ -358,6 +363,31 @@ def test_list_taxonomy_org_filter(self, org_parameter: str, expected_taxonomies:
expected_taxonomies=expected_taxonomies,
)

def test_list_unassigned_taxonomies(self):
"""
Test that passing in "unassigned" query param returns Taxonomies that
are unassigned. i.e. does not belong to any org
"""
self._test_list_taxonomy(
user_attr="staff",
expected_taxonomies=["ot1", "ot2"],
unassigned_parameter=True,
)

def test_list_unassigned_and_org_filter_invalid(self) -> None:
"""
Test that passing "org" and "unassigned" query params should throw an error
"""
url = TAXONOMY_ORG_LIST_URL

self.client.force_authenticate(user=self.user)

query_params = {"org": "orgA", "unassigned": "true"}

response = self.client.get(url, query_params, format="json")

assert response.status_code == status.HTTP_400_BAD_REQUEST

@ddt.data(
("user", (), None),
("staffA", ["tA2", "tBA1", "tBA2"], None),
Expand Down
12 changes: 10 additions & 2 deletions openedx/core/djangoapps/content_tagging/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from openedx_tagging.core.tagging.rest_api.v1.views import ObjectTagView, TaxonomyView
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.request import Request
from rest_framework.response import Response

Expand All @@ -14,6 +14,7 @@
get_taxonomy,
get_taxonomies,
get_taxonomies_for_org,
get_unassigned_taxonomies,
set_taxonomy_orgs,
)
from ...rules import get_admin_orgs
Expand Down Expand Up @@ -55,11 +56,18 @@ def get_queryset(self):
query_params = TaxonomyOrgListQueryParamsSerializer(data=self.request.query_params.dict())
query_params.is_valid(raise_exception=True)
enabled = query_params.validated_data.get("enabled", None)
unassigned = query_params.validated_data.get("unassigned", None)
org = query_params.validated_data.get("org", None)

# Raise an error if both "org" and "unassigned" query params were provided
if "org" in query_params.validated_data and "unassigned" in query_params.validated_data:
raise ValidationError("'org' and 'unassigned' params cannot be both defined")

# If org filtering was requested, then use it, even if the org is invalid/None
org = query_params.validated_data.get("org", None)
if "org" in query_params.validated_data:
queryset = get_taxonomies_for_org(enabled, org)
elif "unassigned" in query_params.validated_data:
queryset = get_unassigned_taxonomies(enabled)
else:
queryset = get_taxonomies(enabled)

Expand Down
7 changes: 7 additions & 0 deletions openedx/core/djangoapps/content_tagging/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ def test_get_taxonomies_for_org(self, org_attr, enabled, expected):
getattr(self, taxonomy_attr) for taxonomy_attr in expected
]

def test_get_unassigned_taxonomies(self):
expected = ["taxonomy_no_orgs"]
taxonomies = list(api.get_unassigned_taxonomies())
assert taxonomies == [
getattr(self, taxonomy_attr) for taxonomy_attr in expected
]

@ddt.data(
("taxonomy_all_orgs", "all_orgs_course_tag"),
("taxonomy_all_orgs", "all_orgs_block_tag"),
Expand Down

0 comments on commit 9e14fa4

Please sign in to comment.