def check_comments_enabled_for_post_with_id(user, post_id): Post = get_post_model() post = Post.objects.select_related('community').get(id=post_id) if post.community_id is not None: if not user.is_staff_of_community_with_name( post.community.name) and not post.comments_enabled: raise ValidationError(_('Comments are disabled for this post'))
def handle(self, *args, **options): Post = get_post_model() PostMedia = get_post_media_model() logger.info(Post.objects.filter(media__isnull=False).count()) posts_to_migrate = Post.objects.filter(Q(media__isnull=False) & Q(media_thumbnail__isnull=True)) migrated_posts = 0 for post in posts_to_migrate.iterator(): with transaction.atomic(): try: post_first_media = post.get_first_media() if post_first_media.type == PostMedia.MEDIA_TYPE_IMAGE: post.media_width = post_first_media.content_object.width post.media_height = post_first_media.content_object.height post.media_thumbnail = post_first_media.content_object.image.file elif post_first_media.type == PostMedia.MEDIA_TYPE_VIDEO: post.media_width = post_first_media.content_object.width post.media_height = post_first_media.content_object.height post.media_thumbnail = post_first_media.content_object.thumbnail.file post.save() except FileNotFoundError as e: print('Ignoring post due to image not found') logger.info(post.media_width) logger.info(post.media_height) logger.info(post.media_thumbnail) migrated_posts = migrated_posts + 1 logger.info('Created media_* for %d posts' % migrated_posts)
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_open_post_with_id(user, post_id): Post = get_post_model() post = Post.objects.select_related('community').get(id=post_id) if post.community_id is None: raise ValidationError(_('Only community posts can be opened/closed')) if not user.is_staff_of_community_with_name(post.community.name): raise ValidationError( _('Only administrators/moderators can open this post'))
def check_can_translate_post_with_id(user, post_id): Post = get_post_model() post = Post.objects.get(id=post_id) if post.is_encircled_post(): raise ValidationError(_('Only public posts can be translated')) if post.text is None: raise ValidationError(_('Post has no text to be translated')) if post.language is None: raise ValidationError( _('Post 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_posts(self): Post = get_post_model() posts = Post.objects.filter(text__isnull=False) for post in posts: try: language = get_language_for_text(post.text) except LangDetectException as e: print('Caught exception while detecting language, skipping') if language: post.language = language post.save() else: print('Could not detect language for id', post.id)
def handle(self, *args, **options): Post = get_post_model() posts_to_migrate = Post.objects.filter(image__isnull=True, media__isnull=False) migrated_posts = 0 for post in posts_to_migrate.iterator(): with transaction.atomic(): post_image = post.get_first_media().content_object if isinstance(post_image, PostImage): post_image.post = post post_image.save() logger.info('Fixed migrated post with id:' + str(post.pk)) migrated_posts = migrated_posts + 1 logger.info('Fixed migrated %d posts' % migrated_posts)
def flush_draft_posts(): """ This job should be scheduled to get all pending draft posts for a day and remove them """ Post = get_post_model() draft_posts = Post.objects.filter(status=Post.STATUS_DRAFT, modified__lt=timezone.now() - timezone.timedelta(days=1)).all() flushed_posts = 0 for draft_post in draft_posts.iterator(): draft_post.delete() flushed_posts = flushed_posts + 1 return 'Flushed %s posts' % str(flushed_posts)
def handle(self, *args, **options): Post = get_post_model() posts_to_process = Post.objects.filter(hashtags__isnull=True, text__isnull=False, text__icontains='#').only( 'id', 'text').all() migrated_posts = 0 for post in _chunked_queryset_iterator(posts_to_process, 100): try: post._process_post_hashtags() except Exception as e: logger.info('Error processing with error %s' % str(e)) logger.info('Processed hashtags for post with id:' + str(post.pk)) migrated_posts = migrated_posts + 1 logger.info('Processed %d posts for hashtags' % migrated_posts)
def process_post_media(post_id): """ This job is called to process post media and mark it as published """ Post = get_post_model() PostMedia = get_post_media_model() post = Post.objects.get(pk=post_id) logger.info('Processing media of post with id: %d' % post_id) post_media_videos = post.media.filter(type=PostMedia.MEDIA_TYPE_VIDEO) for post_media_video in post_media_videos.iterator(): post_video = post_media_video.content_object tasks.convert_video(post_video.file) # This updates the status and created attributes post._publish() logger.info('Processed media of post with id: %d' % post_id)
def check_can_delete_post(user, post): Post = get_post_model() if not user.has_post(post=post): if Post.is_post_with_id_a_community_post(post.pk): # If the comment is in a community, check if we're moderators if not user.is_moderator_of_community_with_name( post.community.name) and not user.is_administrator_of_community_with_name(post.community.name): raise ValidationError( _('Only moderators/administrators can remove community posts.'), ) else: # TODO Not the best place to log this but doing the check for community again on delete is wasteful post.community.create_remove_post_log(source_user=user, target_user=post.creator) else: raise ValidationError( _('You cannot remove a post that does not belong to you') )
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 handle(self, *args, **options): Post = get_post_model() PostMedia = get_post_media_model() posts_to_migrate = Post.objects.filter(image__isnull=False, media__isnull=True) migrated_posts = 0 for post in posts_to_migrate.iterator(): with transaction.atomic(): post_image = post.image PostMedia.create_post_media(type=PostMedia.MEDIA_TYPE_IMAGE, content_object=post_image, post_id=post.pk, order=0) post_image.save() logger.info('Migrated post with id:' + str(post.pk)) migrated_posts = migrated_posts + 1 logger.info('Migrated %d posts' % migrated_posts)
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 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 handle(self, *args, **options): Post = get_post_model() text_query = Q( text__isnull=False, text__icontains='http', ) links_query = Q(links__isnull=True) | Q(links__has_preview=False) posts_to_process = Post.objects.filter(text_query & links_query).only( 'id', 'text').all() migrated_posts = 0 for post in _chunked_queryset_iterator(posts_to_process, 100): try: post._process_post_links() except Exception as e: logger.info('Error processing with error %s' % str(e)) logger.info('Processed links for post with id:' + str(post.pk)) migrated_posts = migrated_posts + 1 logger.info('Processed %d posts for links' % migrated_posts)
def to_representation(self, post): request = self.context.get('request') request_user = request.user reaction_emoji_count = [] if request_user.is_anonymous: if post.public_reactions: Post = get_post_model() reaction_emoji_count = Post.get_emoji_counts_for_post_with_id( post.pk) else: reaction_emoji_count = request_user.get_emoji_counts_for_post_with_id( post.pk) post_reactions_serializer = self.emoji_count_serializer( reaction_emoji_count, many=True, context={ "request": request, 'post': post }) return post_reactions_serializer.data
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_published_posts_query(): # Only retrieve published posts Post = get_post_model() return Q(status=Post.STATUS_PUBLISHED)
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 get_posts_for_user_collection(target_user, source_user, posts_only=None, posts_prefetch_related=None, max_id=None, min_id=None, include_community_posts=False): Post = get_post_model() posts_collection_manager = Post.objects if posts_prefetch_related: posts_collection_manager = posts_collection_manager.prefetch_related( *posts_prefetch_related) if posts_only: posts_collection_manager = posts_collection_manager.only(*posts_only) id_boundary_query = None if max_id: id_boundary_query = make_only_posts_with_max_id(max_id=max_id) elif min_id: id_boundary_query = make_only_posts_with_min_id(min_id=min_id) query = Q( # Created by the target user creator__username=target_user.username, # Not closed is_closed=False, # Not deleted is_deleted=False, # Published status=Post.STATUS_PUBLISHED, ) posts_query = make_circles_posts_query_for_user(user=source_user) if include_community_posts: posts_query.add(make_community_posts_query_for_user(user=source_user), Q.OR) query.add(posts_query, Q.AND) if id_boundary_query: query.add(id_boundary_query, Q.AND) ModeratedObject = get_moderated_object_model() posts_visibility_exclude_query = Q( # Reported posts Q(moderated_object__reports__reporter_id=source_user.pk) | # Approved reported posts Q(moderated_object__status=ModeratedObject.STATUS_APPROVED) | # Posts of users we blocked or that have blocked us Q(creator__blocked_by_users__blocker_id=source_user.pk) | Q(creator__user_blocks__blocked_user_id=source_user.pk) | # Posts of communities banned from Q(community__banned_users__id=source_user.pk)) return posts_collection_manager.filter(query).exclude( posts_visibility_exclude_query).distinct()
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 get(self, request): Post = get_post_model() posts = Post.get_trending_posts()[:30] posts_serializer = AuthenticatedUserPostSerializer( posts, many=True, context={"request": request}) return Response(posts_serializer.data, status=status.HTTP_200_OK)
def post_id_exists(post_id): Post = get_post_model() if not Post.objects.filter(id=post_id).exists(): raise ValidationError(_('The post does not exist.'), )
def get_post_id_for_post_uuid(post_uuid): Post = get_post_model() return Post.get_post_id_for_post_with_uuid(post_uuid=post_uuid)
def post_uuid_exists(post_uuid): Post = get_post_model() if not Post.objects.filter(uuid=post_uuid).exists(): raise NotFound(_('The post does not exist.'), )
def get_post_id_for_post_uuid(post_uuid): Post = get_post_model() post = Post.objects.values('id').get(uuid=post_uuid) return post['id']
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 check_is_not_processing(post): Post = get_post_model() if post.status == Post.STATUS_PROCESSING: raise ValidationError(_('The post is being processed'))
def check_is_not_published(post): Post = get_post_model() if post.status == Post.STATUS_PUBLISHED: raise ValidationError(_('The post is already published'))