Changeset View
Changeset View
Standalone View
Standalone View
profiles/managers.py
- This file was added.
| """Implement custom activity streams.""" | |||||
| from django.contrib.contenttypes.models import ContentType | |||||
| from django.db.models import CharField, Q, QuerySet | |||||
| from django.db.models.functions import Cast | |||||
| from actstream.managers import ActionManager, stream | |||||
| from comments.models import Comment | |||||
| from blog.models import Post | |||||
| class CustomStreamManager(ActionManager): | |||||
| """Defines custom activity streams, such as personal notifications.""" | |||||
| def get_comment_ids(self, user) -> QuerySet: | |||||
| """Retrieve IDs of comments left by given user.""" | |||||
| return ( | |||||
| Comment.objects.filter(user=user) | |||||
| # This is why we can't have nice things: https://code.djangoproject.com/ticket/16055 | |||||
| .annotate(str_id=Cast('id', CharField())) | |||||
| .values_list('str_id', flat=True) | |||||
| ) | |||||
| def get_blog_post_ids(self, user) -> QuerySet: | |||||
| """Retrieve IDs of blog posts authored by a given user.""" | |||||
| return ( | |||||
| Post.objects.filter(author=user) | |||||
| .annotate(str_id=Cast('id', CharField())) | |||||
| .values_list('str_id', flat=True) | |||||
| ) | |||||
| def someone_liked_your_comment_q(self, user) -> Q: | |||||
| """Prepare a query for filtering liked comments of a given user.""" | |||||
| # TODO(anna): what if profile disables notifications about comments? skip this Q | |||||
| verbs = ['liked'] | |||||
| comment_ct = ContentType.objects.get_for_model(Comment) | |||||
| your_comment_ids = self.get_comment_ids(user) | |||||
| return Q( | |||||
| action_object_content_type=comment_ct, | |||||
| action_object_object_id__in=your_comment_ids, | |||||
| verb__in=verbs, | |||||
| ) | |||||
| def someone_replied_to_your_comment_q(self, user) -> Q: | |||||
| """Prepare a query for filtering replies to comments left by a given user.""" | |||||
| # TODO(anna): what if profile disables notifications about comments? skip this Q | |||||
| verbs = ['replied to'] | |||||
| comment_ct = ContentType.objects.get_for_model(Comment) | |||||
| your_comment_ids = self.get_comment_ids(user) | |||||
| return Q( | |||||
| action_object_content_type=comment_ct, | |||||
| action_object_object_id__in=your_comment_ids, | |||||
| verb__in=verbs, | |||||
| ) | |||||
| def someone_commented_on_your_blog_post_q(self, user) -> Q: | |||||
| """Prepare a query for filtering blog posts of a given user targeted by activity.""" | |||||
| verbs = ['added'] | |||||
| post_ct = ContentType.objects.get_for_model(Post) | |||||
| # TODO(anna): what if profile disables notifications about a blog post? filter it out by ID | |||||
| # TODO(anna): what if profile disables notifications about all blog posts? skip this Q | |||||
| your_post_ids = self.get_blog_post_ids(user) | |||||
| return Q(target_content_type=post_ct, target_object_id__in=your_post_ids, verb__in=verbs) | |||||
| @stream | |||||
| def notifications(self, user, **kwargs): | |||||
| """Retrieve all activity that is relevant to a given user. | |||||
| This includes | |||||
| * "someone added a comment on your blog post"; | |||||
| * "someone liked your comment"; | |||||
| * "someone replied to your comment"; | |||||
| * TODO(anna): "someone saved your training section"; | |||||
| * TODO(anna): "someone commented on your training section"; | |||||
| * TODO(anna): "someone commented on your film asset"; | |||||
| """ | |||||
| return self.filter( | |||||
| # TODO(anna): what if notification is read? exclude action ID here or annotate? | |||||
| self.someone_liked_your_comment_q(user) | |||||
| | self.someone_replied_to_your_comment_q(user) | |||||
| | self.someone_commented_on_your_blog_post_q(user), | |||||
| **kwargs, | |||||
| ) | |||||