feat: Add ability to get unassigned taxonomies (#33945)
This adds a query param to fetch unassigned taxonomies, i.e. taxonomies that do not belong to any org.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -46,6 +46,7 @@ class TaxonomyOrgListQueryParamsSerializer(TaxonomyListQueryParamsSerializer):
|
||||
queryset=Organization.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
unassigned: fields.Field = serializers.BooleanField(required=False)
|
||||
|
||||
|
||||
class TaxonomyUpdateOrgBodySerializer(serializers.Serializer):
|
||||
|
||||
@@ -277,7 +277,8 @@ class TestTaxonomyListCreateViewSet(TestTaxonomyObjectsMixin, APITestCase):
|
||||
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
|
||||
@@ -288,7 +289,11 @@ class TestTaxonomyListCreateViewSet(TestTaxonomyObjectsMixin, APITestCase):
|
||||
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")
|
||||
|
||||
@@ -358,6 +363,31 @@ class TestTaxonomyListCreateViewSet(TestTaxonomyObjectsMixin, APITestCase):
|
||||
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),
|
||||
|
||||
@@ -5,7 +5,7 @@ from openedx_tagging.core.tagging import rules as oel_tagging_rules
|
||||
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
|
||||
|
||||
@@ -14,6 +14,7 @@ from ...api import (
|
||||
get_taxonomy,
|
||||
get_taxonomies,
|
||||
get_taxonomies_for_org,
|
||||
get_unassigned_taxonomies,
|
||||
set_taxonomy_orgs,
|
||||
)
|
||||
from ...rules import get_admin_orgs
|
||||
@@ -55,11 +56,18 @@ class TaxonomyOrgView(TaxonomyView):
|
||||
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)
|
||||
|
||||
|
||||
@@ -156,6 +156,13 @@ class TestAPITaxonomy(TestTaxonomyMixin, TestCase):
|
||||
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"),
|
||||
|
||||
Reference in New Issue
Block a user