def check_can_moderate_moderated_object(user, moderated_object): content_object = moderated_object.content_object is_global_moderator = user.is_global_moderator() if is_global_moderator: return PostComment = get_post_comment_model() Post = get_post_model() if isinstance(content_object, Post): if content_object.community: if not user.is_staff_of_community_with_name( community_name=content_object.community.name): raise ValidationError( _('Only community staff can moderate community posts')) else: raise ValidationError( _('Only global moderators can moderate non-community posts')) elif isinstance(content_object, PostComment): if content_object.post.community: if not user.is_staff_of_community_with_name( community_name=content_object.post.community.name): raise ValidationError( _('Only community staff can moderate community post comments' )) else: raise ValidationError( _('Only global moderators can moderate non-community post comments' )) else: raise ValidationError( _('Non global moderators can only moderate posts and post comments.' ))
def check_can_translate_comment_with_id(user, post_comment_id): PostComment = get_post_comment_model() post_comment = PostComment.objects.get(pk=post_comment_id) if post_comment.post.is_encircled_post(): raise ValidationError(_('Only public post comments can be translated')) if post_comment.text is None: raise ValidationError(_('Post comment has no text to be translated')) if post_comment.language is None: raise ValidationError( _('Post comment has no assigned language to be able to translate')) if user.translation_language is None: raise ValidationError( _('User\'s preferred translation language not set'))
def assign_language_comments(self): PostComment = get_post_comment_model() post_comments = PostComment.objects.filter(text__isnull=False) for post_comment in post_comments: try: language = get_language_for_text(post_comment.text) except LangDetectException as e: print('Caught exception while detecting language, skipping') if language: post_comment.language = language post_comment.save() else: print('Could not detect language for id', post_comment.id)
def handle(self, *args, **options): PostComment = get_post_comment_model() comments_to_process = PostComment.objects.filter(hashtags__isnull=True, text__icontains='#').only('id', 'text').all() migrated_postComments = 0 for comment in _chunked_queryset_iterator(comments_to_process, 100): try: comment._process_post_comment_hashtags() except Exception as e: logger.info('Failed to process hashtags with error %s' % str(e)) logger.info('Processed hashtags for post comment with id:' + str(comment.pk)) migrated_postComments = migrated_postComments + 1 logger.info('Processed %d post comments for hashtags' % migrated_postComments)
def check_can_edit_comment_with_id_for_post(user, post_comment_id, post): check_can_see_post(user=user, post=post) # Check that the comment belongs to the post PostComment = get_post_comment_model() if not PostComment.objects.filter(id=post_comment_id, post_id=post.pk).exists(): raise ValidationError( _('The comment does not belong to the specified post.') ) if post.community and post.is_closed: is_administrator = user.is_administrator_of_community_with_name(post.community.name) is_moderator = user.is_moderator_of_community_with_name(post.community.name) if not is_moderator and not is_administrator: raise ValidationError( _('Only administrators/moderators can edit a closed post.') )
def check_can_delete_comment_with_id_for_post(user, post_comment_id, post): check_can_see_post(user=user, post=post) # Check that the comment belongs to the post PostComment = get_post_comment_model() Post = get_post_model() if not PostComment.objects.filter(id=post_comment_id, post_id=post.pk).exists(): raise ValidationError( _('The comment does not belong to the specified post.')) is_comment_creator = user.posts_comments.filter( id=post_comment_id).exists() if post.community: is_moderator = user.is_moderator_of_community_with_name( post.community.name) is_administrator = user.is_administrator_of_community_with_name( post.community.name) if not is_administrator and not is_moderator: if post.is_closed: raise ValidationError( _('Only moderators/administrators can remove closed community posts.' ), ) elif not is_comment_creator: raise ValidationError( _('You cannot remove a comment that does not belong to you' )) else: # is admin or mod post_comment = PostComment.objects.select_related('commenter').get( pk=post_comment_id) if post_comment.parent_comment is not None: post.community.create_remove_post_comment_reply_log( source_user=user, target_user=post_comment.commenter) else: post.community.create_remove_post_comment_log( source_user=user, target_user=post_comment.commenter) elif not post.creator_id == user.pk and not is_comment_creator: # not a community post raise ValidationError( _('You cannot remove a comment that does not belong to you'))
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 to_representation(self, post_comment): request = self.context.get('request') request_user = request.user if request_user.is_anonymous: PostComment = get_post_comment_model() reaction_emoji_count = PostComment.get_emoji_counts_for_post_comment_with_id( post_comment.pk) else: reaction_emoji_count = request_user.get_emoji_counts_for_post_comment( post_comment=post_comment) post_comment_reactions_serializer = self.emoji_count_serializer( reaction_emoji_count, many=True, context={ "request": request, 'post_comment': post_comment }) return post_comment_reactions_serializer.data
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 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 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 post_comment_id_exists_for_post_with_uuid(post_comment_id, post_uuid): PostComment = get_post_comment_model() if not PostComment.objects.filter(id=post_comment_id, post__uuid=post_uuid).exists(): raise ValidationError(_('The post comment does not exist.'), )