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:
Yusuf Musleh
2023-12-18 22:28:04 +03:00
committed by GitHub
parent 9a46bdfe22
commit 9e14fa4ac3
5 changed files with 68 additions and 4 deletions

View File

@@ -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,

View File

@@ -46,6 +46,7 @@ class TaxonomyOrgListQueryParamsSerializer(TaxonomyListQueryParamsSerializer):
queryset=Organization.objects.all(),
required=False,
)
unassigned: fields.Field = serializers.BooleanField(required=False)
class TaxonomyUpdateOrgBodySerializer(serializers.Serializer):

View File

@@ -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),

View File

@@ -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)

View File

@@ -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"),