Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2605,6 +2605,21 @@ def save(self, *args, **kwargs):

super().save(*args, **kwargs)

def mark_live(self):
"""
Marks this submission as the live submission for the channel,
and marks any previously live submissions as approved but not live.
"""
CommunityLibrarySubmission.objects.filter(
channel=self.channel,
status=community_library_submission.STATUS_LIVE,
).update(
status=community_library_submission.STATUS_APPROVED,
)

self.status = community_library_submission.STATUS_LIVE
self.save()

@classmethod
def filter_view_queryset(cls, queryset, user):
if user.is_anonymous:
Expand Down
36 changes: 36 additions & 0 deletions contentcuration/contentcuration/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,42 @@ def test_filter_edit_queryset__admin(self):
)
self.assertQuerysetContains(queryset, pk=submission_a.id)

def test_mark_live(self):
submission_a = testdata.community_library_submission()
submission_b = testdata.community_library_submission()

channel = submission_a.channel
channel.version = 2
submission_b.channel = channel

submission_a.channel_version = 1
submission_a.status = community_library_submission.STATUS_LIVE
submission_a.save()

submission_b.channel_version = 2
submission_b.author = submission_a.author
submission_b.status = community_library_submission.STATUS_APPROVED
submission_b.save()

submission_other_channel = testdata.community_library_submission()
submission_other_channel.status = community_library_submission.STATUS_LIVE
submission_other_channel.save()

submission_b.mark_live()

submission_a.refresh_from_db()
submission_b.refresh_from_db()
submission_other_channel.refresh_from_db()

self.assertEqual(
submission_a.status, community_library_submission.STATUS_APPROVED
)
self.assertEqual(submission_b.status, community_library_submission.STATUS_LIVE)
self.assertEqual(
submission_other_channel.status,
community_library_submission.STATUS_LIVE,
)


class AssessmentItemTestCase(PermissionQuerysetTestCase):
@property
Expand Down
117 changes: 117 additions & 0 deletions contentcuration/contentcuration/tests/viewsets/test_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
from contentcuration import models
from contentcuration import models as cc
from contentcuration.constants import channel_history
from contentcuration.constants import community_library_submission
from contentcuration.models import Change
from contentcuration.models import CommunityLibrarySubmission
from contentcuration.models import ContentNode
from contentcuration.models import Country
from contentcuration.tasks import apply_channel_changes_task
from contentcuration.tests import testdata
from contentcuration.tests.base import StudioAPITestCase
from contentcuration.tests.viewsets.base import generate_create_event
Expand All @@ -24,6 +29,9 @@
from contentcuration.tests.viewsets.base import SyncTestMixin
from contentcuration.viewsets.channel import _unpublished_changes_query
from contentcuration.viewsets.sync.constants import CHANNEL
from contentcuration.viewsets.sync.utils import (
generate_added_to_community_library_event,
)


class SyncTestCase(SyncTestMixin, StudioAPITestCase):
Expand Down Expand Up @@ -517,6 +525,115 @@ def test_publish_next_with_incomplete_staging_tree(self):
modified_channel = models.Channel.objects.get(id=channel.id)
self.assertEqual(modified_channel.staging_tree.published, False)

def test_sync_added_to_community_library_change(self):
# Syncing the change from the frontend should be disallowed
self.client.force_authenticate(self.admin_user)

channel = testdata.channel()
channel.version = 1
channel.public = True
channel.save()

added_to_community_library_change = generate_added_to_community_library_event(
key=channel.id,
channel_version=1,
)
response = self.sync_changes([added_to_community_library_change])

self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(len(response.json()["allowed"]), 0, response.content)
self.assertEqual(len(response.json()["disallowed"]), 1, response.content)

@mock.patch("contentcuration.viewsets.channel.export_channel_to_kolibri_public")
def test_process_added_to_community_library_change(self, mock_export_func):
# Creating the change on the backend should be supported
self.client.force_authenticate(self.admin_user)

editor_user = testdata.user("channel@editor.com")

channel = testdata.channel()
channel.version = 2
channel.public = False
channel.editors.add(editor_user)
channel.save()

current_live_submission = CommunityLibrarySubmission.objects.create(
channel=channel,
channel_version=1,
author=editor_user,
status=community_library_submission.STATUS_LIVE,
)
new_submission = CommunityLibrarySubmission.objects.create(
channel=channel,
channel_version=2,
author=editor_user,
status=community_library_submission.STATUS_APPROVED,
)

categories = {
"category1": True,
"category2": True,
}

country1 = Country.objects.create(code="C1", name="Country 1")
country2 = Country.objects.create(code="C2", name="Country 2")
countries = [country1, country2]
country_codes = [country.code for country in countries]

added_to_community_library_change = generate_added_to_community_library_event(
key=channel.id,
channel_version=2,
categories=categories,
country_codes=country_codes,
)
Change.create_change(
added_to_community_library_change, created_by_id=self.admin_user.id
)

# This task will run immediately thanks to SyncTestMixin
apply_channel_changes_task.fetch_or_enqueue(
user=self.admin_user,
channel_id=channel.id,
)

# We cannot easily use the assert_called_once_with method here
# because we are not checking countries for strict equality,
# so we need to check the call arguments manually
mock_export_func.assert_called_once()

(call_args, call_kwargs) = mock_export_func.call_args
self.assertEqual(len(call_args), 0)
self.assertCountEqual(
call_kwargs.keys(),
[
"channel_id",
"channel_version",
"categories",
"countries",
"public",
],
)
self.assertEqual(call_kwargs["channel_id"], channel.id)
self.assertEqual(call_kwargs["channel_version"], 2)
self.assertCountEqual(call_kwargs["categories"], categories.keys())

# The countries argument used when creating the mapper is in fact
# not a list, but a QuerySet, but it contains the same elements
self.assertCountEqual(call_kwargs["countries"], countries)
self.assertEqual(call_kwargs["public"], False)

# Check that the current submission became the live one
current_live_submission.refresh_from_db()
new_submission.refresh_from_db()
self.assertEqual(
current_live_submission.status,
community_library_submission.STATUS_APPROVED,
)
self.assertEqual(
new_submission.status,
community_library_submission.STATUS_LIVE,
)


class CRUDTestCase(StudioAPITestCase):
@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
from contentcuration.constants import (
community_library_submission as community_library_submission_constants,
)
from contentcuration.models import Change
from contentcuration.models import CommunityLibrarySubmission
from contentcuration.tests import testdata
from contentcuration.tests.base import StudioAPITestCase
from contentcuration.viewsets.sync.constants import ADDED_TO_COMMUNITY_LIBRARY


def reverse_with_query(
Expand Down Expand Up @@ -59,21 +61,21 @@ def setUp(self):
self.country2 = testdata.country(name="Country 2", code="C2")

self.channel_with_submission1 = testdata.channel()
self.channel_with_submission1.public = True
self.channel_with_submission1.public = False
self.channel_with_submission1.version = 1
self.channel_with_submission1.editors.add(self.author_user)
self.channel_with_submission1.editors.add(self.editor_user)
self.channel_with_submission1.save()

self.channel_with_submission2 = testdata.channel()
self.channel_with_submission2.public = True
self.channel_with_submission2.public = False
self.channel_with_submission2.version = 1
self.channel_with_submission2.editors.add(self.author_user)
self.channel_with_submission2.editors.add(self.editor_user)
self.channel_with_submission2.save()

self.channel_without_submission = testdata.channel()
self.channel_without_submission.public = True
self.channel_without_submission.public = False
self.channel_without_submission.version = 1
self.channel_without_submission.editors.add(self.author_user)
self.channel_without_submission.editors.add(self.editor_user)
Expand All @@ -86,6 +88,13 @@ def setUp(self):
self.unpublished_channel.editors.add(self.editor_user)
self.unpublished_channel.save()

self.public_channel = testdata.channel()
self.public_channel.public = True
self.public_channel.version = 1
self.public_channel.editors.add(self.author_user)
self.public_channel.editors.add(self.editor_user)
self.public_channel.save()

self.existing_submission1 = testdata.community_library_submission()
self.existing_submission1.channel = self.channel_with_submission1
self.existing_submission1.author = self.author_user
Expand Down Expand Up @@ -132,6 +141,18 @@ def test_create_submission__unpublished_channel(self):
)
self.assertEqual(response.status_code, 400, response.content)

def test_create_submission__public_channel(self):
self.client.force_authenticate(user=self.editor_user)
submission = self.new_submission_metadata
submission["channel"] = self.public_channel.id

response = self.client.post(
reverse("community-library-submission-list"),
submission,
format="json",
)
self.assertEqual(response.status_code, 400, response.content)

def test_create_submission__explicit_channel_version(self):
self.client.force_authenticate(user=self.editor_user)
submission_metadata = self.new_submission_metadata
Expand Down Expand Up @@ -317,7 +338,7 @@ def test_update_submission__is_editor(self):
)
self.assertEqual(response.status_code, 404, response.content)

def test_update_submission__is_admin(self):
def test_update_submission__is_admin__change_countries(self):
self.client.force_authenticate(user=self.admin_user)
response = self.client.put(
reverse(
Expand All @@ -335,6 +356,28 @@ def test_update_submission__is_admin(self):
self.assertEqual(updated_submission.countries.count(), 1)
self.assertEqual(updated_submission.countries.first().code, "C2")

def test_update_submission__is_admin__keep_countries(self):
self.client.force_authenticate(user=self.admin_user)

updated_submission_metadata = self.updated_submission_metadata.copy()
updated_submission_metadata.pop("countries")

response = self.client.put(
reverse(
"community-library-submission-detail",
args=[self.existing_submission1.id],
),
updated_submission_metadata,
format="json",
)
self.assertEqual(response.status_code, 200, response.content)

updated_submission = CommunityLibrarySubmission.objects.get(
id=self.existing_submission1.id
)
self.assertEqual(updated_submission.countries.count(), 1)
self.assertEqual(updated_submission.countries.first().code, "C1")

def test_update_submission__change_channel(self):
self.client.force_authenticate(user=self.admin_user)
submission_metadata = self.updated_submission_metadata
Expand Down Expand Up @@ -628,7 +671,10 @@ def test_resolve_submission__editor(self):
)
self.assertEqual(response.status_code, 403, response.content)

def test_resolve_submission__accept_correct(self):
@mock.patch(
"contentcuration.viewsets.community_library_submission.apply_channel_changes_task"
)
def test_resolve_submission__accept_correct(self, apply_task_mock):
self.client.force_authenticate(user=self.admin_user)
response = self.client.post(
reverse(
Expand All @@ -652,7 +698,21 @@ def test_resolve_submission__accept_correct(self):
self.assertEqual(resolved_submission.resolved_by, self.admin_user)
self.assertEqual(resolved_submission.date_resolved, self.resolved_time)

def test_resolve_submission__reject_correct(self):
self.assertTrue(
Change.objects.filter(
channel=self.submission.channel,
change_type=ADDED_TO_COMMUNITY_LIBRARY,
).exists()
)
apply_task_mock.fetch_or_enqueue.assert_called_once_with(
self.admin_user,
channel_id=self.submission.channel.id,
)

@mock.patch(
"contentcuration.viewsets.community_library_submission.apply_channel_changes_task"
)
def test_resolve_submission__reject_correct(self, apply_task_mock):
self.client.force_authenticate(user=self.admin_user)
response = self.client.post(
reverse(
Expand Down Expand Up @@ -680,6 +740,14 @@ def test_resolve_submission__reject_correct(self):
self.assertEqual(resolved_submission.resolved_by, self.admin_user)
self.assertEqual(resolved_submission.date_resolved, self.resolved_time)

self.assertFalse(
Change.objects.filter(
channel=self.submission.channel,
change_type=ADDED_TO_COMMUNITY_LIBRARY,
).exists()
)
apply_task_mock.fetch_or_enqueue.assert_not_called()

def test_resolve_submission__reject_missing_resolution_reason(self):
self.client.force_authenticate(user=self.admin_user)
metadata = self.resolve_reject_metadata.copy()
Expand Down
Loading