Skip to content

Commit 17d931e

Browse files
authored
Merge pull request #5292 from Jakoma02/adminchannelviewset-submission-fields
Community library submission fields and filters in AdminChannelViewSet
2 parents d544732 + 23f5b15 commit 17d931e

File tree

4 files changed

+229
-17
lines changed

4 files changed

+229
-17
lines changed

contentcuration/contentcuration/tests/helpers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from importlib import import_module
2+
from urllib.parse import urlencode
23

34
import mock
45
from celery import states
6+
from django.urls import reverse
57
from django_celery_results.models import TaskResult
68
from search.models import ContentNodeFullTextSearch
79

@@ -53,3 +55,19 @@ def __new__(cls, *args, **kwargs):
5355
return mock.Mock(spec_set=cls)
5456

5557
return MockClass()
58+
59+
60+
def reverse_with_query(
61+
viewname, urlconf=None, args=None, kwargs=None, current_app=None, query=None
62+
):
63+
"""
64+
This helper wraps the Django `reverse` function to support the `query` argument.
65+
This argument is supported natively since Django 5.2, so when Django is updated
66+
above this version, this helper can be removed.
67+
"""
68+
url = reverse(
69+
viewname, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app
70+
)
71+
if query:
72+
return f"{url}?{urlencode(query)}"
73+
return url

contentcuration/contentcuration/tests/viewsets/test_channel.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from contentcuration.tasks import apply_channel_changes_task
2020
from contentcuration.tests import testdata
2121
from contentcuration.tests.base import StudioAPITestCase
22+
from contentcuration.tests.helpers import reverse_with_query
2223
from contentcuration.tests.viewsets.base import generate_create_event
2324
from contentcuration.tests.viewsets.base import generate_delete_event
2425
from contentcuration.tests.viewsets.base import generate_deploy_channel_event
@@ -673,6 +674,168 @@ def test_fetch_admin_channels_invalid_filter(self):
673674
)
674675
self.assertEqual(response.status_code, 200, response.content)
675676

677+
def test_admin_channel_detail__latest_community_library_submission__exists(self):
678+
older_submission = testdata.community_library_submission()
679+
older_submission.channel.version = 2
680+
older_submission.channel_version = 1
681+
older_submission.status = community_library_submission.STATUS_LIVE
682+
older_submission.channel.save()
683+
older_submission.save()
684+
685+
latest_submission = CommunityLibrarySubmission.objects.create(
686+
channel=older_submission.channel,
687+
channel_version=2,
688+
author=older_submission.author,
689+
)
690+
691+
self.client.force_authenticate(self.admin_user)
692+
response = self.client.get(
693+
reverse(
694+
"admin-channels-detail", kwargs={"pk": older_submission.channel.id}
695+
),
696+
format="json",
697+
)
698+
self.assertEqual(response.status_code, 200, response.content)
699+
self.assertEqual(
700+
response.data["latest_community_library_submission_id"],
701+
latest_submission.id,
702+
)
703+
self.assertEqual(
704+
response.data["latest_community_library_submission_status"],
705+
community_library_submission.STATUS_PENDING,
706+
)
707+
self.assertTrue(response.data["has_any_live_community_library_submission"])
708+
709+
def test_admin_channel_detail__latest_community_library_submission__none_exist(
710+
self,
711+
):
712+
channel = testdata.channel()
713+
714+
self.client.force_authenticate(self.admin_user)
715+
response = self.client.get(
716+
reverse("admin-channels-detail", kwargs={"pk": channel.id}),
717+
format="json",
718+
)
719+
self.assertEqual(response.status_code, 200, response.content)
720+
self.assertIsNone(response.data["latest_community_library_submission_id"])
721+
self.assertIsNone(response.data["latest_community_library_submission_status"])
722+
self.assertFalse(response.data["has_any_live_community_library_submission"])
723+
724+
def test_admin_channel_filter__latest_community_library_submission_status__any(
725+
self,
726+
):
727+
self.client.force_authenticate(user=self.admin_user)
728+
729+
submission = testdata.community_library_submission()
730+
731+
response = self.client.get(
732+
reverse_with_query(
733+
"admin-channels-list",
734+
query={
735+
"id__in": submission.channel.id,
736+
},
737+
),
738+
format="json",
739+
)
740+
self.assertEqual(response.status_code, 200, response.content)
741+
self.assertEqual(len(response.data), 1)
742+
743+
def test_admin_channel_filter__latest_community_library_submission_status__multiple(
744+
self,
745+
):
746+
self.client.force_authenticate(user=self.admin_user)
747+
748+
submission1 = testdata.community_library_submission()
749+
submission1.status = community_library_submission.STATUS_LIVE
750+
submission1.save()
751+
752+
submission2 = testdata.community_library_submission()
753+
submission2.status = community_library_submission.STATUS_PENDING
754+
submission2.save()
755+
756+
submission3 = testdata.community_library_submission()
757+
submission3.status = community_library_submission.STATUS_APPROVED
758+
submission3.save()
759+
760+
response = self.client.get(
761+
reverse_with_query(
762+
"admin-channels-list",
763+
query=[
764+
(
765+
"latest_community_library_submission_status",
766+
community_library_submission.STATUS_LIVE,
767+
),
768+
(
769+
"latest_community_library_submission_status",
770+
community_library_submission.STATUS_PENDING,
771+
),
772+
],
773+
),
774+
format="json",
775+
)
776+
self.assertEqual(response.status_code, 200, response.content)
777+
self.assertCountEqual(
778+
[ch["id"] for ch in response.data],
779+
[
780+
submission1.channel.id,
781+
submission2.channel.id,
782+
],
783+
)
784+
785+
def test_admin_channel_filter__community_library_live(self):
786+
self.client.force_authenticate(user=self.admin_user)
787+
788+
submission1 = testdata.community_library_submission()
789+
submission1.channel.version = 2
790+
submission1.channel.save()
791+
submission1.status = community_library_submission.STATUS_LIVE
792+
submission1.channel_version = 1
793+
submission1.save()
794+
795+
CommunityLibrarySubmission.objects.create(
796+
channel=submission1.channel,
797+
channel_version=2,
798+
author=submission1.author,
799+
status=community_library_submission.STATUS_PENDING,
800+
)
801+
802+
other_channel_submission = testdata.community_library_submission()
803+
other_channel_submission.status = community_library_submission.STATUS_PENDING
804+
other_channel_submission.save()
805+
806+
response = self.client.get(
807+
reverse_with_query(
808+
"admin-channels-list",
809+
query={"community_library_live": True},
810+
),
811+
format="json",
812+
)
813+
self.assertEqual(response.status_code, 200, response.content)
814+
self.assertCountEqual(
815+
[ch["id"] for ch in response.data],
816+
[submission1.channel.id],
817+
)
818+
819+
def test_admin_channel_filter__has_community_library_submission(self):
820+
self.client.force_authenticate(user=self.admin_user)
821+
822+
submission = testdata.community_library_submission()
823+
824+
testdata.channel() # Another channel without submission
825+
826+
response = self.client.get(
827+
reverse_with_query(
828+
"admin-channels-list",
829+
query={"has_community_library_submission": True},
830+
),
831+
format="json",
832+
)
833+
self.assertEqual(response.status_code, 200, response.content)
834+
self.assertCountEqual(
835+
[ch["id"] for ch in response.data],
836+
[submission.channel.id],
837+
)
838+
676839
def test_create_channel(self):
677840
user = testdata.user()
678841
self.client.force_authenticate(user=user)

contentcuration/contentcuration/tests/viewsets/test_community_library_submission.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import datetime
22
from unittest import mock
3-
from urllib.parse import urlencode
43

54
import pytz
65
from django.urls import reverse
@@ -12,25 +11,10 @@
1211
from contentcuration.models import CommunityLibrarySubmission
1312
from contentcuration.tests import testdata
1413
from contentcuration.tests.base import StudioAPITestCase
14+
from contentcuration.tests.helpers import reverse_with_query
1515
from contentcuration.viewsets.sync.constants import ADDED_TO_COMMUNITY_LIBRARY
1616

1717

18-
def reverse_with_query(
19-
viewname, urlconf=None, args=None, kwargs=None, current_app=None, query=None
20-
):
21-
"""
22-
This helper wraps the Django `reverse` function to support the `query` argument.
23-
This argument is supported natively since Django 5.2, so when Django is updated
24-
above this version, this helper can be removed.
25-
"""
26-
url = reverse(
27-
viewname, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app
28-
)
29-
if query:
30-
return f"{url}?{urlencode(query)}"
31-
return url
32-
33-
3418
class CRUDTestCase(StudioAPITestCase):
3519
@property
3620
def new_submission_metadata(self):

contentcuration/contentcuration/viewsets/channel.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.conf import settings
99
from django.db import IntegrityError
1010
from django.db.models import Exists
11+
from django.db.models import FilteredRelation
1112
from django.db.models import OuterRef
1213
from django.db.models import Q
1314
from django.db.models import Subquery
@@ -1049,6 +1050,16 @@ def annotate_queryset(self, queryset):
10491050

10501051

10511052
class AdminChannelFilter(BaseChannelFilter):
1053+
latest_community_library_submission_status = CharFilter(
1054+
method="filter_latest_community_library_submission_status"
1055+
)
1056+
community_library_live = BooleanFilter(
1057+
method="filter_community_library_live",
1058+
)
1059+
has_community_library_submission = BooleanFilter(
1060+
method="filter_has_community_library_submission",
1061+
)
1062+
10521063
def filter_keywords(self, queryset, name, value):
10531064
keywords = value.split(" ")
10541065
editors_first_name = reduce(
@@ -1066,6 +1077,20 @@ def filter_keywords(self, queryset, name, value):
10661077
| editors_email
10671078
)
10681079

1080+
def filter_latest_community_library_submission_status(self, queryset, name, value):
1081+
values = self.request.query_params.getlist(name)
1082+
if values:
1083+
return queryset.filter(
1084+
latest_community_library_submission__status__in=values
1085+
)
1086+
return queryset
1087+
1088+
def filter_community_library_live(self, queryset, name, value):
1089+
return queryset.filter(has_any_live_community_library_submission=True)
1090+
1091+
def filter_has_community_library_submission(self, queryset, name, value):
1092+
return queryset.filter(latest_community_library_submission__isnull=False)
1093+
10691094

10701095
class AdminChannelSerializer(ChannelSerializer):
10711096
"""
@@ -1097,6 +1122,8 @@ class AdminChannelViewSet(ChannelViewSet, RESTUpdateModelMixin, RESTDestroyModel
10971122
"created": "main_tree__created",
10981123
"source_url": format_source_url,
10991124
"demo_server_url": format_demo_server_url,
1125+
"latest_community_library_submission_id": "latest_community_library_submission__id",
1126+
"latest_community_library_submission_status": "latest_community_library_submission__status",
11001127
}
11011128

11021129
values = (
@@ -1116,6 +1143,9 @@ class AdminChannelViewSet(ChannelViewSet, RESTUpdateModelMixin, RESTDestroyModel
11161143
"source_url",
11171144
"demo_server_url",
11181145
"primary_token",
1146+
"latest_community_library_submission__id",
1147+
"latest_community_library_submission__status",
1148+
"has_any_live_community_library_submission",
11191149
)
11201150

11211151
def perform_destroy(self, instance):
@@ -1145,11 +1175,28 @@ def get_queryset(self):
11451175
channel_main_tree_nodes = ContentNode.objects.filter(
11461176
tree_id=OuterRef("main_tree__tree_id")
11471177
)
1178+
latest_community_library_submission_id = Subquery(
1179+
CommunityLibrarySubmission.objects.filter(channel_id=OuterRef("id"))
1180+
.order_by("-date_created")
1181+
.values("id")[:1]
1182+
)
11481183
queryset = Channel.objects.all().annotate(
11491184
modified=Subquery(
11501185
channel_main_tree_nodes.values("modified").order_by("-modified")[:1]
11511186
),
11521187
primary_token=primary_token_subquery,
1188+
latest_community_library_submission=FilteredRelation(
1189+
"community_library_submissions",
1190+
condition=Q(
1191+
community_library_submissions__id=latest_community_library_submission_id,
1192+
),
1193+
),
1194+
has_any_live_community_library_submission=Exists(
1195+
CommunityLibrarySubmission.objects.filter(
1196+
channel_id=OuterRef("id"),
1197+
status=community_library_submission_constants.STATUS_LIVE,
1198+
)
1199+
),
11531200
)
11541201
return queryset
11551202

0 commit comments

Comments
 (0)