def check_can_add_moderator_with_username_to_community_with_name(user, username, community_name): if not user.is_administrator_of_community_with_name(community_name=community_name): raise ValidationError( _('Only administrators of the community can add other moderators.'), ) Community = get_community_model() if Community.is_user_with_username_administrator_of_community_with_name(username=username, community_name=community_name): raise ValidationError( _('User is an administrator.'), ) if Community.is_user_with_username_moderator_of_community_with_name(username=username, community_name=community_name): raise ValidationError( _('User is already a moderator.'), ) if not Community.is_user_with_username_member_of_community_with_name(username=username, community_name=community_name): raise ValidationError( _('Can\'t make moderator a user that is not part of the community.'), )
def test_not_displays_private_communities(self): """ should not display private communities and return 200 """ user = make_user() amount_of_communities = 5 Community = get_community_model() for i in range(0, amount_of_communities): community_owner = make_user() community = make_community(creator=community_owner, type=Community.COMMUNITY_TYPE_PRIVATE) headers = make_authentication_headers_for_user(user) url = self._get_url() response = self.client.get(url, **headers, format='multipart') self.assertEqual(response.status_code, status.HTTP_200_OK) response_communities = json.loads(response.content) self.assertEqual(len(response_communities), 0)
def check_can_invite_user_with_username_to_community_with_name( user, username, community_name): if not user.is_member_of_community_with_name( community_name=community_name): raise ValidationError( _('You can only invite people to a community you are member of.'), ) if user.has_invited_user_with_username_to_community_with_name( username=username, community_name=community_name): raise ValidationError( _('You have already invited this user to join the community.'), ) Community = get_community_model() if Community.is_user_with_username_member_of_community_with_name( username=username, community_name=community_name): raise ValidationError( _('The user is already part of the community.'), ) if not Community.is_community_with_name_invites_enabled( community_name=community_name) and not ( user.is_administrator_of_community_with_name( community_name=community_name) or user.is_moderator_of_community_with_name( community_name=community_name)): raise ValidationError( _('Invites for this community are not enabled. Only administrators & moderators can invite.' ), )
def check_can_exclude_community(user, community): if user.has_excluded_community_with_name(community_name=community.name): raise ValidationError( _('You have already marked this community as excluded.'), ) Community = get_community_model() if community.type == Community.COMMUNITY_TYPE_PRIVATE: raise ValidationError( _('Private communities are always excluded from top posts.'), )
def check_can_get_posts_for_community_with_name(user, community_name): check_is_not_banned_from_community_with_name(user=user, community_name=community_name) Community = get_community_model() if Community.is_community_with_name_private( community_name=community_name) and not user.is_member_of_community_with_name( community_name=community_name): raise ValidationError( _('The community is private. You must become a member to retrieve its posts.'), )
def check_can_get_community_with_name_members(user, community_name): check_is_not_banned_from_community_with_name(user=user, community_name=community_name) Community = get_community_model() if Community.is_community_with_name_private(community_name=community_name): if not user.is_member_of_community_with_name(community_name=community_name): raise ValidationError( _('Can\'t see the members of a private community.'), )
def _get_trending_posts_query(cls): trending_posts_query = Q(created__gte=timezone.now() - timedelta( hours=12)) Community = get_community_model() trending_posts_sources_query = Q(community__type=Community.COMMUNITY_TYPE_PUBLIC) trending_posts_query.add(trending_posts_sources_query, Q.AND) return trending_posts_query
def check_can_remove_moderator_with_username_to_community_with_name( user, username, community_name): if not user.is_administrator_of_community_with_name( community_name=community_name): raise ValidationError( _('Only administrators of the community can remove other moderators.' ), ) Community = get_community_model() if not Community.is_user_with_username_moderator_of_community_with_name( username=username, community_name=community_name): raise ValidationError(_('User to remove is not an moderator.'), )
def get(self, request): query_params = request.query_params.dict() serializer = self.serializer_class(data=query_params) serializer.is_valid(raise_exception=True) data = serializer.data category_name = data.get('category') Community = get_community_model() communities = Community.get_trending_communities(category_name=category_name)[:30] posts_serializer = CommunitiesCommunitySerializer(communities, many=True, context={"request": request}) return Response(posts_serializer.data, status=status.HTTP_200_OK)
def get_trending_posts(cls): Community = get_community_model() trending_posts_query = Q(created__gte=timezone.now() - timedelta(hours=12)) trending_posts_sources_query = Q( community__type=Community.COMMUNITY_TYPE_PUBLIC) trending_posts_query.add(trending_posts_sources_query, Q.AND) return cls.objects.annotate( Count('reactions')).filter(trending_posts_query).order_by( '-reactions__count', '-created')
def check_can_unban_user_with_username_from_community_with_name( user, username, community_name): if not user.is_administrator_of_community_with_name( community_name=community_name ) and not user.is_moderator_of_community_with_name( community_name=community_name): raise ValidationError( _('Only community administrators & moderators can ban community members.' ), ) Community = get_community_model() if not Community.is_user_with_username_banned_from_community_with_name( username=username, community_name=community_name): raise ValidationError(_('Can\'t unban a not-banned user.'), )
def _get_trending_posts_query(cls): trending_posts_query = Q(created__gte=timezone.now() - timedelta(hours=12)) Community = get_community_model() trending_posts_sources_query = Q( community__type=Community.COMMUNITY_TYPE_PUBLIC, status=cls.STATUS_PUBLISHED, is_closed=False, is_deleted=False) trending_posts_query.add(trending_posts_sources_query, Q.AND) return trending_posts_query
def check_can_join_community_with_name(user, community_name): if user.is_banned_from_community_with_name(community_name): raise ValidationError( 'You can\'t join a community you have been banned from.') if user.is_member_of_community_with_name(community_name): raise ValidationError( _('You are already a member of the community.'), ) Community = get_community_model() if Community.is_community_with_name_private(community_name=community_name): if not user.is_invited_to_community_with_name( community_name=community_name): raise ValidationError( _('You are not invited to join this community.'), )
def create_post(cls, creator, circles_ids=None, community_name=None, image=None, text=None, video=None, created=None, is_draft=False): if not community_name and not circles_ids: raise ValidationError( _('A post requires circles or a community to be posted to.')) if community_name and circles_ids: raise ValidationError( _('A post cannot be posted both to a community and to circles.' )) post = Post.objects.create(creator=creator, created=created) if image and video: raise ValidationError( _('A post must have an image or a video, not both.')) if text: post.text = text post.language = get_language_for_text(text) if image: post.add_media(file=image) elif video: post.add_media(file=video) if circles_ids: post.circles.add(*circles_ids) else: Community = get_community_model() post.community = Community.objects.get(name=community_name) # If on create we have a video or image, we automatically publish it # Backwards compat reasons. if not is_draft: post.publish() else: post.save() return post
def create_post(cls, creator, circles_ids=None, community_name=None, image=None, text=None, video=None, created=None): if not community_name and not circles_ids: raise ValidationError( _('A post requires circles or a community to be posted to.')) if community_name and circles_ids: raise ValidationError( _('A post cannot be posted both to a community and to circles.' )) if not text and not image and not video: raise ValidationError(_('A post requires text or an image/video.')) if image and video: raise ValidationError( _('A post must have an image or a video, not both.')) post = Post.objects.create(creator=creator, created=created) if text: post.text = text post.language = get_language_for_text(text) if image: PostImage.create_post_image(image=image, post_id=post.pk) if video: PostVideo.create_post_video(video=video, post_id=post.pk) if circles_ids: post.circles.add(*circles_ids) else: Community = get_community_model() post.community = Community.objects.get(name=community_name) post.save() return post
def unverify_with_actor_with_id(self, actor_id): current_verified = self.verified self.verified = False ModeratedObjectVerifiedChangedLog.create_moderated_object_verified_changed_log( changed_from=current_verified, changed_to=self.verified, moderated_object_id=self.pk, actor_id=actor_id) self.user_penalties.all().delete() content_object = self.content_object Post = get_post_model() PostComment = get_post_comment_model() Community = get_community_model() moderation_severity = self.category.severity if (isinstance(content_object, Post) or isinstance(content_object, PostComment) or isinstance( content_object, Community)) or ( isinstance(content_object, User) and moderation_severity == ModerationCategory.SEVERITY_CRITICAL): content_object.unsoft_delete() self.save() content_object.save()
def check_can_ban_user_with_username_from_community_with_name(user, username, community_name): if not user.is_administrator_of_community_with_name( community_name=community_name) and not user.is_moderator_of_community_with_name( community_name=community_name): raise ValidationError( _('Only community administrators & moderators can ban community members.'), ) Community = get_community_model() if Community.is_user_with_username_banned_from_community_with_name(username=username, community_name=community_name): raise ValidationError( _('User is already banned'), ) if Community.is_user_with_username_moderator_of_community_with_name(username=username, community_name=community_name) or Community.is_user_with_username_administrator_of_community_with_name( username=username, community_name=community_name): raise ValidationError( _('You can\'t ban moderators or administrators of the community'), )
def approve_with_actor_with_id(self, actor_id): Post = get_post_model() PostComment = get_post_comment_model() Community = get_community_model() User = get_user_model() content_object = self.content_object moderation_severity = self.category.severity current_status = self.status self.status = ModeratedObject.STATUS_APPROVED ModeratedObjectStatusChangedLog.create_moderated_object_status_changed_log( changed_from=current_status, changed_to=self.status, moderated_object_id=self.pk, actor_id=actor_id) if isinstance(content_object, Post) or \ isinstance(content_object, PostComment) or \ isinstance(content_object, Community): content_object.delete_notifications() if isinstance(content_object, User) and moderation_severity == ModerationCategory.SEVERITY_CRITICAL: content_object.delete_outgoing_notifications() self.save()
def check_community_name_not_taken(user, community_name): Community = get_community_model() if Community.is_name_taken(community_name): raise ValidationError( _('A community with that name already exists.'), )
def verify_with_actor_with_id(self, actor_id): current_verified = self.verified self.verified = True ModeratedObjectVerifiedChangedLog.create_moderated_object_verified_changed_log( changed_from=current_verified, changed_to=self.verified, moderated_object_id=self.pk, actor_id=actor_id) Post = get_post_model() Hashtag = get_hashtag_model() PostComment = get_post_comment_model() Community = get_community_model() User = get_user_model() ModerationPenalty = get_moderation_penalty_model() content_object = self.content_object moderation_severity = self.category.severity penalty_targets = None if self.is_approved(): if isinstance(content_object, User): penalty_targets = [content_object] elif isinstance(content_object, Post): penalty_targets = [content_object.creator] elif isinstance(content_object, PostComment): penalty_targets = [content_object.commenter] elif isinstance(content_object, Community): penalty_targets = content_object.get_staff_members() elif isinstance(content_object, ModeratedObject): penalty_targets = content_object.get_reporters() elif isinstance(content_object, Hashtag): penalty_targets = [] for penalty_target in penalty_targets: duration_of_penalty = None penalties_count = penalty_target.count_moderation_penalties_for_moderation_severity( moderation_severity=moderation_severity) + 1 if moderation_severity == ModerationCategory.SEVERITY_CRITICAL: duration_of_penalty = timezone.timedelta(weeks=5000) elif moderation_severity == ModerationCategory.SEVERITY_HIGH: duration_of_penalty = timezone.timedelta(days=penalties_count ** 4) elif moderation_severity == ModerationCategory.SEVERITY_MEDIUM: duration_of_penalty = timezone.timedelta(hours=penalties_count ** 3) elif moderation_severity == ModerationCategory.SEVERITY_LOW: duration_of_penalty = timezone.timedelta(minutes=penalties_count ** 2) moderation_expiration = timezone.now() + duration_of_penalty ModerationPenalty.create_suspension_moderation_penalty(moderated_object=self, user_id=penalty_target.pk, expiration=moderation_expiration) if (isinstance(content_object, Post) or isinstance(content_object, PostComment) or isinstance( content_object, Community)) or ( isinstance(content_object, User) and moderation_severity == ModerationCategory.SEVERITY_CRITICAL): content_object.soft_delete() if moderation_severity == ModerationCategory.SEVERITY_CRITICAL and isinstance(content_object, Post): # We have hashes content_object.delete_media() content_object.save() self.save()
def __init__(self): PostReaction = get_post_reaction_model() PostComment = get_post_comment_model() User = get_user_model() Emoji = get_emoji_model() Post = get_post_model() CommunityInvite = get_community_invite_model() Community = get_community_model() class NotificationUserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ( 'id', 'username', ) class NotificationEmojiSerializer(serializers.ModelSerializer): class Meta: model = Emoji fields = ( 'id', 'keyword', ) class NotificationPostSerializer(serializers.ModelSerializer): class Meta: model = Post fields = ( 'id', 'uuid', ) class NotificationPostReactionSerializer(serializers.ModelSerializer): reactor = NotificationUserSerializer() emoji = NotificationEmojiSerializer() post = NotificationPostSerializer() class Meta: model = PostReaction fields = ('id', 'reactor', 'emoji', 'post') self.NotificationPostReactionSerializer = NotificationPostReactionSerializer class NotificationCommunitySerializer(serializers.ModelSerializer): class Meta: model = Community fields = ('id', 'name', 'title') class NotificationCommunityInviteSerializer(serializers.ModelSerializer ): creator = NotificationUserSerializer() invited_user = NotificationUserSerializer() community = NotificationCommunitySerializer() class Meta: model = CommunityInvite fields = ('id', 'creator', 'invited_user', 'community') self.NotificationCommunityInviteSerializer = NotificationCommunityInviteSerializer class NotificationPostCommentSerializer(serializers.ModelSerializer): commenter = NotificationUserSerializer() post = NotificationPostSerializer() class Meta: model = PostComment fields = ('id', 'commenter', 'post') self.NotificationPostCommentSerializer = NotificationPostCommentSerializer class NotificationPostCommentReplySerializer( serializers.ModelSerializer): commenter = NotificationUserSerializer() post = NotificationPostSerializer() class Meta: model = PostComment fields = ('id', 'commenter', 'post') self.NotificationPostCommentReplySerializer = NotificationPostCommentReplySerializer class FollowNotificationSerializer(serializers.Serializer): following_user = NotificationUserSerializer() self.FollowNotificationSerializer = FollowNotificationSerializer class ConnectionRequestNotificationSerializer(serializers.Serializer): connection_requester = NotificationUserSerializer() self.ConnectionRequestNotificationSerializer = ConnectionRequestNotificationSerializer
def community_name_exists(community_name): Community = get_community_model() if not Community.objects.filter(name=community_name).exists(): raise NotFound(_('No community with the provided name exists.'), )
def community_name_not_taken_validator(community_name): Community = get_community_model() if Community.is_name_taken(community_name): raise ValidationError(_('Community name already taken.'), )
def curate_top_posts(): """ Curates the top posts. This job should be scheduled to be run every n hours. """ Post = get_post_model() Community = get_community_model() PostComment = get_post_comment_model() ModeratedObject = get_moderated_object_model() TopPost = get_top_post_model() logger.info('Processing top posts at %s...' % timezone.now()) top_posts_community_query = Q(top_post__isnull=True) top_posts_community_query.add( Q(community__isnull=False, community__type=Community.COMMUNITY_TYPE_PUBLIC), Q.AND) top_posts_community_query.add( Q(is_closed=False, is_deleted=False, status=Post.STATUS_PUBLISHED), Q.AND) top_posts_community_query.add( ~Q(moderated_object__status=ModeratedObject.STATUS_APPROVED), Q.AND) top_posts_criteria_query = Q(total_comments_count__gte=settings.MIN_UNIQUE_TOP_POST_COMMENTS_COUNT) | \ Q(reactions_count__gte=settings.MIN_UNIQUE_TOP_POST_REACTIONS_COUNT) posts_select_related = 'community' posts_prefetch_related = ('comments__commenter', 'reactions__reactor') posts_only = ('id', 'status', 'is_deleted', 'is_closed', 'community__type') posts = Post.objects. \ select_related(posts_select_related). \ prefetch_related(*posts_prefetch_related). \ only(*posts_only). \ filter(top_posts_community_query). \ annotate(total_comments_count=Count('comments__commenter_id'), reactions_count=Count('reactions__reactor_id')). \ filter(top_posts_criteria_query) top_posts_objects = [] total_checked_posts = 0 total_curated_posts = 0 for post in _chunked_queryset_iterator(posts, 1000): total_checked_posts = total_checked_posts + 1 if not post.reactions_count >= settings.MIN_UNIQUE_TOP_POST_REACTIONS_COUNT: unique_comments_count = PostComment.objects.filter(post=post). \ values('commenter_id'). \ annotate(user_comments_count=Count('commenter_id')).count() if unique_comments_count >= settings.MIN_UNIQUE_TOP_POST_COMMENTS_COUNT: top_post = _add_post_to_top_post(post=post) if top_post is not None: top_posts_objects.append(top_post) else: top_post = _add_post_to_top_post(post=post) if top_post is not None: top_posts_objects.append(top_post) if len(top_posts_objects) > 1000: TopPost.objects.bulk_create(top_posts_objects) total_curated_posts += len(top_posts_objects) top_posts_objects = [] if len(top_posts_objects) > 0: total_curated_posts += len(top_posts_objects) TopPost.objects.bulk_create(top_posts_objects) return 'Checked: %d. Curated: %d' % (total_checked_posts, total_curated_posts)
def clean_top_posts(): """ Cleans up top posts, that no longer meet the criteria. """ Post = get_post_model() Community = get_community_model() TopPost = get_top_post_model() PostComment = get_post_comment_model() ModeratedObject = get_moderated_object_model() # if any of these is true, we will remove the top post top_posts_community_query = Q( post__community__type=Community.COMMUNITY_TYPE_PRIVATE) top_posts_community_query.add(Q(post__is_closed=True), Q.OR) top_posts_community_query.add(Q(post__is_deleted=True), Q.OR) top_posts_community_query.add(Q(post__status=Post.STATUS_DRAFT), Q.OR) top_posts_community_query.add(Q(post__status=Post.STATUS_PROCESSING), Q.OR) top_posts_community_query.add( Q(post__moderated_object__status=ModeratedObject.STATUS_APPROVED), Q.OR) # counts less than minimum top_posts_criteria_query = Q(total_comments_count__lt=settings.MIN_UNIQUE_TOP_POST_COMMENTS_COUNT) & \ Q(reactions_count__lt=settings.MIN_UNIQUE_TOP_POST_REACTIONS_COUNT) posts_select_related = 'post__community' posts_prefetch_related = ('post__comments__commenter', 'post__reactions__reactor') posts_only = ('post__id', 'post__status', 'post__is_deleted', 'post__is_closed', 'post__community__type') direct_removable_top_posts = TopPost.objects.select_related(posts_select_related). \ prefetch_related(*posts_prefetch_related). \ only(*posts_only). \ filter(top_posts_community_query). \ annotate(total_comments_count=Count('post__comments__commenter_id'), reactions_count=Count('post__reactions__reactor_id')). \ filter(top_posts_criteria_query) # bulk delete all that definitely dont meet the criteria anymore direct_removable_top_posts.delete() # Now we need to only check the ones where the unique comments count might have dropped, # while all other criteria is fine top_posts_community_query = Q( post__community__isnull=False, post__community__type=Community.COMMUNITY_TYPE_PUBLIC) top_posts_community_query.add( Q(post__is_closed=False, post__is_deleted=False, post__status=Post.STATUS_PUBLISHED), Q.AND) top_posts_community_query.add( ~Q(post__moderated_object__status=ModeratedObject.STATUS_APPROVED), Q.AND) top_posts_criteria_query = Q(total_comments_count__gte=settings.MIN_UNIQUE_TOP_POST_COMMENTS_COUNT) | \ Q(reactions_count__gte=settings.MIN_UNIQUE_TOP_POST_REACTIONS_COUNT) top_posts = TopPost.objects.select_related(posts_select_related). \ prefetch_related(*posts_prefetch_related). \ only(*posts_only). \ filter(top_posts_community_query). \ annotate(total_comments_count=Count('post__comments__commenter_id'), reactions_count=Count('post__reactions__reactor_id')). \ filter(top_posts_criteria_query) delete_ids = [] for top_post in _chunked_queryset_iterator(top_posts, 1000): if not top_post.reactions_count >= settings.MIN_UNIQUE_TOP_POST_REACTIONS_COUNT: unique_comments_count = PostComment.objects.filter(post=top_post.post). \ values('commenter_id'). \ annotate(user_comments_count=Count('commenter_id')).count() if unique_comments_count < settings.MIN_UNIQUE_TOP_POST_COMMENTS_COUNT: delete_ids.append(top_post.pk) # bulk delete ids TopPost.objects.filter(id__in=delete_ids).delete()
def make_only_public_community_posts_query(): Community = get_community_model() return Q(community__type=Community.COMMUNITY_TYPE_PUBLIC, )
def community_name_exists(community_name): Community = get_community_model() if not Community.community_with_name_exists(community_name=community_name): raise NotFound( _('No community with the provided name exists.'), )