-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2128 from ResearchHub/feed-comment-signals
feat: Add comment signal handlers for feed
- Loading branch information
Showing
4 changed files
with
209 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from .bounty_signals import * # noqa: F401, F403 | ||
from .comment_signals import * # noqa: F401, F403 | ||
from .paper_signals import * # noqa: F401, F403 | ||
from .post_signals import * # noqa: F401, F403 |
This file contains 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,74 @@ | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.db import transaction | ||
from django.db.models.signals import post_save | ||
from django.dispatch import receiver | ||
|
||
from feed.models import FeedEntry | ||
from feed.tasks import create_feed_entry, delete_feed_entry | ||
from researchhub_comment.related_models.rh_comment_model import RhCommentModel | ||
|
||
""" | ||
Signal handlers for Comment model. | ||
The signal handlers are responsbile for creating and deleting feed entries | ||
when comments are created and removed, respectively. | ||
""" | ||
|
||
|
||
@receiver(post_save, sender=RhCommentModel) | ||
def handle_comment_created_or_removed(sender, instance, created, **kwargs): | ||
""" | ||
When a comment is created or removed, create or delete a feed entry. | ||
""" | ||
comment = instance | ||
|
||
if created: | ||
_handle_comment_created(comment) | ||
elif comment.is_removed: | ||
_handle_comment_removed(comment) | ||
|
||
|
||
def _handle_comment_created(comment): | ||
# Validate that the comment is associated with a unified document with hubs | ||
if not getattr(comment, "unified_document", None) or not hasattr( | ||
comment.unified_document, "hubs" | ||
): | ||
return | ||
|
||
tasks = [ | ||
create_feed_entry.apply_async( | ||
args=( | ||
comment.id, | ||
ContentType.objects.get_for_model(comment).id, | ||
FeedEntry.PUBLISH, | ||
hub.id, | ||
ContentType.objects.get_for_model(hub).id, | ||
comment.created_by.id, | ||
), | ||
priority=1, | ||
) | ||
for hub in comment.unified_document.hubs.all() | ||
] | ||
transaction.on_commit(lambda: [task() for task in tasks]) | ||
|
||
|
||
def _handle_comment_removed(comment): | ||
# Validate that the comment is associated with a unified document with hubs | ||
if not getattr(comment, "unified_document", None) or not hasattr( | ||
comment.unified_document, "hubs" | ||
): | ||
return | ||
|
||
tasks = [ | ||
delete_feed_entry.apply_async( | ||
args=( | ||
comment.id, | ||
ContentType.objects.get_for_model(comment).id, | ||
hub.id, | ||
ContentType.objects.get_for_model(hub).id, | ||
), | ||
priority=1, | ||
) | ||
for hub in comment.unified_document.hubs.all() | ||
] | ||
transaction.on_commit(lambda: [task() for task in tasks]) |
This file contains 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 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 unittest.mock import MagicMock, call, patch | ||
|
||
from django.contrib.contenttypes.models import ContentType | ||
from django.test import TestCase | ||
|
||
from feed.models import FeedEntry | ||
from feed.tests.test_views import User | ||
from hub.models import Hub | ||
from paper.related_models.paper_model import Paper | ||
from researchhub_comment.constants.rh_comment_thread_types import GENERIC_COMMENT | ||
from researchhub_comment.related_models.rh_comment_model import RhCommentModel | ||
from researchhub_comment.related_models.rh_comment_thread_model import ( | ||
RhCommentThreadModel, | ||
) | ||
from researchhub_document.related_models.constants import document_type | ||
from researchhub_document.related_models.researchhub_unified_document_model import ( | ||
ResearchhubUnifiedDocument, | ||
) | ||
|
||
|
||
class CommentSignalsTests(TestCase): | ||
|
||
def setUp(self): | ||
self.user = User.objects.create_user(username="user1") | ||
self.hub1 = Hub.objects.create(name="hub1") | ||
self.hub2 = Hub.objects.create(name="hub2") | ||
self.unified_document = ResearchhubUnifiedDocument.objects.create( | ||
document_type=document_type.DISCUSSION, | ||
) | ||
self.unified_document.hubs.add(self.hub1) | ||
self.unified_document.hubs.add(self.hub2) | ||
self.paper = Paper.objects.create( | ||
title="paper1", unified_document=self.unified_document | ||
) | ||
|
||
self.content_type = ContentType.objects.get_for_model(Paper) | ||
self.thread = RhCommentThreadModel.objects.create( | ||
thread_type=GENERIC_COMMENT, | ||
content_type=self.content_type, | ||
object_id=self.paper.id, | ||
created_by=self.user, | ||
) | ||
self.comment = RhCommentModel.objects.create( | ||
comment_content_json={"ops": [{"insert": "comment1"}]}, | ||
created_by=self.user, | ||
thread=self.thread, | ||
) | ||
|
||
@patch("feed.signals.comment_signals.create_feed_entry") | ||
@patch("feed.signals.comment_signals.transaction") | ||
def test_handle_comment_created_signal( | ||
self, mock_transaction, mock_create_feed_entry | ||
): | ||
""" | ||
Test that a feed entry is created when a comment is created. | ||
""" | ||
# Arrange | ||
mock_transaction.on_commit = lambda func: func() | ||
mock_create_feed_entry.apply_async = MagicMock() | ||
|
||
# Act | ||
comment = RhCommentModel.objects.create( | ||
comment_content_json={"ops": [{"insert": "comment1"}]}, | ||
created_by=self.user, | ||
thread=self.thread, | ||
) | ||
|
||
# Assert | ||
mock_create_feed_entry.apply_async.assert_has_calls( | ||
[ | ||
call( | ||
args=( | ||
comment.id, | ||
ContentType.objects.get_for_model(RhCommentModel).id, | ||
FeedEntry.PUBLISH, | ||
self.hub1.id, | ||
ContentType.objects.get_for_model(Hub).id, | ||
self.user.id, | ||
), | ||
priority=1, | ||
), | ||
call( | ||
args=( | ||
comment.id, | ||
ContentType.objects.get_for_model(RhCommentModel).id, | ||
FeedEntry.PUBLISH, | ||
self.hub2.id, | ||
ContentType.objects.get_for_model(Hub).id, | ||
self.user.id, | ||
), | ||
priority=1, | ||
), | ||
] | ||
) | ||
|
||
@patch("feed.signals.comment_signals.delete_feed_entry") | ||
@patch("feed.signals.comment_signals.transaction") | ||
def test_handle_comment_removed_signal( | ||
self, mock_transaction, mock_delete_feed_entry | ||
): | ||
""" | ||
Test that a feed entry is deleted when a comment is removed. | ||
""" | ||
# Arrange | ||
mock_transaction.on_commit = lambda func: func() | ||
mock_delete_feed_entry.apply_async = MagicMock() | ||
|
||
# Act | ||
self.comment.is_removed = True | ||
self.comment.save() | ||
|
||
# Assert | ||
mock_delete_feed_entry.apply_async.assert_has_calls( | ||
[ | ||
call( | ||
args=( | ||
self.comment.id, | ||
ContentType.objects.get_for_model(RhCommentModel).id, | ||
self.hub1.id, | ||
ContentType.objects.get_for_model(Hub).id, | ||
), | ||
priority=1, | ||
), | ||
call( | ||
args=( | ||
self.comment.id, | ||
ContentType.objects.get_for_model(RhCommentModel).id, | ||
self.hub2.id, | ||
ContentType.objects.get_for_model(Hub).id, | ||
), | ||
priority=1, | ||
), | ||
] | ||
) |