-
Notifications
You must be signed in to change notification settings - Fork 228
Implements backend core to incorporate Flagging Mechanism #4537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ozer550
merged 6 commits into
learningequality:unstable
from
ozer550:implement-feedback-core-serializers
Aug 20, 2024
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
2c42a16
add serializer feedback serializers
ozer550 99c14c9
Add Flagged Viewset
ozer550 d7d59d5
add skeleton for viewset tests
ozer550 b9e3368
add some tests
ozer550 b661805
implement viewset and CRUD tests
ozer550 17b5195
add retrieve Permissions
ozer550 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,11 +229,13 @@ def random_string(chars=10): | |
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(chars)) | ||
|
||
|
||
def user(email='[email protected]'): | ||
def user(email='[email protected]', feature_flags=None): | ||
user, is_new = cc.User.objects.get_or_create(email=email) | ||
if is_new: | ||
user.set_password('password') | ||
user.is_active = True | ||
if feature_flags is not None: | ||
user.feature_flags = feature_flags | ||
user.save() | ||
return user | ||
|
||
|
134 changes: 134 additions & 0 deletions
134
contentcuration/contentcuration/tests/viewsets/test_flagged.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
from django.urls import reverse | ||
from le_utils.constants import content_kinds | ||
|
||
from contentcuration.models import FlagFeedbackEvent | ||
from contentcuration.tests import testdata | ||
from contentcuration.tests.base import StudioAPITestCase | ||
|
||
|
||
class CRUDTestCase(StudioAPITestCase): | ||
@property | ||
def flag_feedback_object(self): | ||
return { | ||
'context': {'spam': 'Spam or misleading'}, | ||
'contentnode_id': self.contentNode.id, | ||
'content_id': self.contentNode.content_id, | ||
'target_channel_id': self.channel.id, | ||
'user': self.user.id, | ||
'feedback_type': 'FLAGGED', | ||
'feedback_reason': 'Some reason provided by the user' | ||
} | ||
|
||
def setUp(self): | ||
super(CRUDTestCase, self).setUp() | ||
self.contentNode = testdata.node( | ||
{ | ||
"kind_id": content_kinds.VIDEO, | ||
"title": "Suspicious Video content", | ||
}, | ||
) | ||
self.channel = testdata.channel() | ||
self.user = testdata.user(feature_flags={"test_dev_feature": True}) | ||
|
||
def test_create_flag_event(self): | ||
self.client.force_authenticate(user=self.user) | ||
flagged_content = self.flag_feedback_object | ||
response = self.client.post( | ||
reverse("flagged-list"), flagged_content, format="json", | ||
) | ||
self.assertEqual(response.status_code, 201, response.content) | ||
|
||
def test_create_flag_event_fails_for_flag_test_dev_feature_disabled(self): | ||
flagged_content = self.flag_feedback_object | ||
self.user.feature_flags = {'test_dev_feature': False} | ||
self.user.save() | ||
self.client.force_authenticate(user=self.user) | ||
response = self.client.post( | ||
reverse("flagged-list"), flagged_content, format="json", | ||
) | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_create_flag_event_fails_for_flag_test_dev_feature_None(self): | ||
flagged_content = self.flag_feedback_object | ||
self.user.feature_flags = None | ||
self.user.save() | ||
self.client.force_authenticate(user=self.user) | ||
response = self.client.post( | ||
reverse("flagged-list"), flagged_content, format="json", | ||
) | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_create_flag_event_fails_for_unauthorized_user(self): | ||
flagged_content = self.flag_feedback_object | ||
response = self.client.post( | ||
reverse("flagged-list"), flagged_content, format="json", | ||
) | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_list_flagged_content_super_admin(self): | ||
self.user.is_admin = True | ||
self.user.save() | ||
self.client.force_authenticate(self.user) | ||
response = self.client.get(reverse("flagged-list"), format="json") | ||
self.assertEqual(response.status_code, 200, response.content) | ||
|
||
def test_retreive_fails_for_normal_user(self): | ||
self.client.force_authenticate(user=self.user) | ||
flag_feedback_object = FlagFeedbackEvent.objects.create( | ||
**{ | ||
'context': {'spam': 'Spam or misleading'}, | ||
'contentnode_id': self.contentNode.id, | ||
'content_id': self.contentNode.content_id, | ||
'target_channel_id': self.channel.id, | ||
'feedback_type': 'FLAGGED', | ||
'feedback_reason': 'Some reason provided by the user' | ||
}, | ||
user=self.user, | ||
) | ||
response = self.client.get(reverse("flagged-detail", kwargs={"pk": flag_feedback_object.id}), format="json") | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_list_fails_for_normal_user(self): | ||
self.client.force_authenticate(user=self.user) | ||
response = self.client.get(reverse("flagged-list"), format="json") | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_list_fails_for_user_dev_feature_enabled(self): | ||
response = self.client.get(reverse("flagged-list"), format="json") | ||
self.assertEqual(response.status_code, 403, response.content) | ||
|
||
def test_destroy_flagged_content_super_admin(self): | ||
self.user.is_admin = True | ||
self.user.save() | ||
self.client.force_authenticate(self.user) | ||
flag_feedback_object = FlagFeedbackEvent.objects.create( | ||
**{ | ||
'context': {'spam': 'Spam or misleading'}, | ||
'contentnode_id': self.contentNode.id, | ||
'content_id': self.contentNode.content_id, | ||
'target_channel_id': self.channel.id, | ||
'feedback_type': 'FLAGGED', | ||
'feedback_reason': 'Some reason provided by the user' | ||
}, | ||
user=self.user, | ||
) | ||
response = self.client.delete(reverse("flagged-detail", kwargs={"pk": flag_feedback_object.id}), format="json") | ||
self.assertEqual(response.status_code, 204, response.content) | ||
|
||
def test_destroy_flagged_content_fails_for_user_with_feature_flag_disabled(self): | ||
self.user.feature_flags = {'test_dev_feature': False} | ||
self.user.save() | ||
self.client.force_authenticate(user=self.user) | ||
flag_feedback_object = FlagFeedbackEvent.objects.create( | ||
**{ | ||
'context': {'spam': 'Spam or misleading'}, | ||
'contentnode_id': self.contentNode.id, | ||
'content_id': self.contentNode.content_id, | ||
'target_channel_id': self.channel.id, | ||
'feedback_type': 'FLAGGED', | ||
'feedback_reason': 'Some reason provided by the user' | ||
}, | ||
user=self.user, | ||
) | ||
response = self.client.delete(reverse("flagged-detail", kwargs={"pk": flag_feedback_object.id}), format="json") | ||
self.assertEqual(response.status_code, 403, response.content) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from rest_framework import permissions | ||
from rest_framework import serializers | ||
from rest_framework import viewsets | ||
from rest_framework.permissions import IsAuthenticated | ||
|
||
from contentcuration.models import FlagFeedbackEvent | ||
|
||
|
||
class IsAdminForListAndDestroy(permissions.BasePermission): | ||
def has_permission(self, request, view): | ||
# only allow list and destroy of flagged content to admins | ||
if view.action in ['list', 'destroy', 'retrieve']: | ||
try: | ||
return request.user and request.user.is_admin | ||
except AttributeError: | ||
return False | ||
if request.user.check_feature_flag('test_dev_feature'): | ||
return True | ||
return False | ||
|
||
|
||
class BaseFeedbackSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
fields = ['id', 'context', 'contentnode_id', 'content_id'] | ||
read_only_fields = ['id'] | ||
|
||
|
||
class BaseFeedbackEventSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
fields = ['user', 'target_channel_id'] | ||
read_only_fields = ['user'] | ||
|
||
|
||
class BaseFeedbackInteractionEventSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
fields = ['feedback_type', 'feedback_reason'] | ||
|
||
|
||
class FlagFeedbackEventSerializer(BaseFeedbackSerializer, BaseFeedbackEventSerializer, BaseFeedbackInteractionEventSerializer): | ||
class Meta: | ||
model = FlagFeedbackEvent | ||
fields = BaseFeedbackSerializer.Meta.fields + BaseFeedbackEventSerializer.Meta.fields + BaseFeedbackInteractionEventSerializer.Meta.fields | ||
|
||
|
||
class FlagFeedbackEventViewSet(viewsets.ModelViewSet): | ||
queryset = FlagFeedbackEvent.objects.all() | ||
serializer_class = FlagFeedbackEventSerializer | ||
permission_classes = [IsAuthenticated, IsAdminForListAndDestroy] |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.