def handle(self, *args, **options): SearchIndex.objects.all().delete() indexed_comment_count = 0 indexed_post_count = 0 indexed_user_count = 0 for chunk in chunked_queryset( Comment.visible_objects().filter(is_deleted=False, post__is_visible=True) ): for comment in chunk: self.stdout.write(f"Indexing comment: {comment.id}") SearchIndex.update_comment_index(comment) indexed_comment_count += 1 for chunk in chunked_queryset( Post.visible_objects().filter(is_shadow_banned=False) ): for post in chunk: self.stdout.write(f"Indexing post: {post.slug}") SearchIndex.update_post_index(post) indexed_post_count += 1 for chunk in chunked_queryset( User.objects.filter(moderation_status=User.MODERATION_STATUS_APPROVED) ): for user in chunk: self.stdout.write(f"Indexing user: {user.slug}") SearchIndex.update_user_index(user) SearchIndex.update_user_tags(user) indexed_user_count += 1 self.stdout.write( f"Done 🥙 " f"Comments: {indexed_comment_count} Posts: {indexed_post_count} Users: {indexed_user_count}" )
def bookmarks(request): user = request.me posts = Post.visible_objects().filter( bookmarks__user=user).order_by('-bookmarks__created_at').all() return render(request, "bookmarks.html", { "posts": paginate(request, posts), })
def command_top(update: Update, context: CallbackContext) -> None: # Top posts top_posts = Post.visible_objects()\ .filter(published_at__gte=datetime.utcnow() - TOP_TIMEDELTA)\ .filter(Q(is_approved_by_moderator=True) | Q(upvotes__gte=settings.COMMUNITY_APPROVE_UPVOTES)) \ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST])\ .order_by("-upvotes")[:5] # Hot posts hot_posts = Post.visible_objects()\ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST]) \ .exclude(id__in=[p.id for p in top_posts]) \ .order_by("-hotness")[:3] # Top intros top_intros = Post.visible_objects()\ .filter(type=Post.TYPE_INTRO, published_at__gte=datetime.utcnow() - TOP_TIMEDELTA)\ .select_related("author")\ .order_by("-upvotes")[:3] # Top comments top_comment = Comment.visible_objects() \ .filter(created_at__gte=datetime.utcnow() - TOP_TIMEDELTA) \ .filter(is_deleted=False)\ .exclude(post__type=Post.TYPE_BATTLE) \ .select_related("author") \ .order_by("-upvotes") \ .first() update.effective_chat.send_message( render_html_message( template="top.html", top_posts=top_posts, hot_posts=hot_posts, top_intros=top_intros, top_comment=top_comment, ), parse_mode=ParseMode.HTML, disable_web_page_preview=True, )
def handle(self, *args, **options): new_posts = Post.visible_objects()\ .filter( is_approved_by_moderator=True, published_at__gte=datetime.utcnow() - timedelta(hours=24), )\ .exclude(type=Post.TYPE_INTRO)\ .order_by("-upvotes")[:6] send_telegram_message( chat=ADMIN_CHAT, text=render_html_message("good_morning.html", posts=new_posts, greetings=random.choice(DUMB_GREETINGS)), ) self.stdout.write("Done 🥙")
def handle(self, *args, **options): LinkedPost.objects.all().delete() posts = Post.visible_objects().exclude( type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST]) for post in posts: print(f"Parsing post: {post.slug}") LinkedPost.create_links_from_text(post, post.text) del posts comments = Comment.visible_objects() for comment in comments: print(f"Parsing comment: {comment.id}") LinkedPost.create_links_from_text(comment.post, comment.text) print("Done 🥙")
def handle(self, *args, **options): SearchIndex.objects.all().delete() for comment in Comment.visible_objects().filter(is_deleted=False, post__is_visible=True): self.stdout.write(f"Indexing comment: {comment.id}") SearchIndex.update_comment_index(comment) for post in Post.visible_objects().filter(is_shadow_banned=False): self.stdout.write(f"Indexing post: {post.slug}") SearchIndex.update_post_index(post) for user in User.objects.filter( moderation_status=User.MODERATION_STATUS_APPROVED): self.stdout.write(f"Indexing user: {user.slug}") SearchIndex.update_user_index(user) SearchIndex.update_user_tags(user) self.stdout.write("Done 🥙")
def command_random(update: Update, context: CallbackContext) -> None: post = None attempt = 0 while not post and attempt < 5: attempt += 1 random_date = settings.LAUNCH_DATE + timedelta( seconds=randint(0, int((datetime.utcnow() - settings.LAUNCH_DATE).total_seconds())), ) post = Post.visible_objects() \ .filter(published_at__lte=random_date, published_at__gte=random_date - timedelta(days=2)) \ .filter(is_approved_by_moderator=True) \ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST]) \ .order_by("?") \ .first() update.effective_chat.send_message( render_html_message("channel_post_announce.html", post=post), parse_mode=ParseMode.HTML, disable_web_page_preview=True, )
def weekly_digest(request): end_date = datetime.utcnow() start_date = end_date - timedelta(days=7) if settings.DEBUG: start_date = end_date - timedelta(days=1000) created_at_condition = dict(created_at__gte=start_date, created_at__lte=end_date) published_at_condition = dict(published_at__gte=start_date, published_at__lte=end_date) # New users intros = Post.visible_objects()\ .filter(type=Post.TYPE_INTRO, **published_at_condition)\ .order_by("-upvotes") newbie_count = User.objects\ .filter( moderation_status=User.MODERATION_STATUS_APPROVED, **created_at_condition )\ .count() # Best posts featured_post = Post.visible_objects()\ .exclude(type=Post.TYPE_INTRO)\ .filter( label__isnull=False, label__code="top_week", **published_at_condition )\ .order_by("-upvotes")\ .first() posts = Post.visible_objects()\ .filter(is_approved_by_moderator=True, **published_at_condition)\ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST])\ .exclude(id=featured_post.id if featured_post else None)\ .exclude(label__isnull=False, label__code="ad")\ .exclude(is_shadow_banned=True)\ .order_by("-upvotes") post_count = posts.count() posts = posts[:12] # Video of the week top_video_comment = Comment.visible_objects()\ .filter(**created_at_condition)\ .filter(is_deleted=False)\ .filter(upvotes__gte=3)\ .filter(Q(text__contains="https://youtu.be/") | Q(text__contains="youtube.com/watch"))\ .order_by("-upvotes")\ .first() top_video_post = None if not top_video_comment: top_video_post = Post.visible_objects() \ .filter(type=Post.TYPE_LINK, upvotes__gte=3) \ .filter(**published_at_condition) \ .filter(Q(url__contains="https://youtu.be/") | Q(url__contains="youtube.com/watch")) \ .order_by("-upvotes") \ .first() # Best comments comments = Comment.visible_objects() \ .filter(**created_at_condition) \ .filter(is_deleted=False)\ .exclude(post__type=Post.TYPE_BATTLE)\ .exclude(post__is_visible=False)\ .exclude(id=top_video_comment.id if top_video_comment else None)\ .order_by("-upvotes")[:3] # Intro from author author_intro = GodSettings.objects.first().digest_intro if not author_intro and not posts and not comments: raise Http404() # New achievements achievements = UserAchievement.objects\ .filter(**created_at_condition)\ .select_related("user", "achievement")\ .order_by("achievement") # required for grouping # Exclude footer for archive is_footer_excluded = "no_footer" in request.GET return render( request, "emails/weekly.html", { "posts": posts, "comments": comments, "intros": intros, "achievements": achievements, "newbie_count": newbie_count, "post_count": post_count, "top_video_comment": top_video_comment, "top_video_post": top_video_post, "featured_post": featured_post, "author_intro": author_intro, "issue_number": (end_date - settings.LAUNCH_DATE).days // 7, "is_footer_excluded": is_footer_excluded })
def daily_digest(request, user_slug): user = get_object_or_404(User, slug=user_slug) end_date = datetime.utcnow() start_date = end_date - timedelta(days=1) if end_date.weekday() == 1: # we don't have daily on sundays and mondays, we need to include all these posts at tuesday start_date = end_date - timedelta(days=3) if settings.DEBUG: start_date = end_date - timedelta(days=1000) created_at_condition = dict(created_at__gte=start_date, created_at__lte=end_date) published_at_condition = dict(published_at__gte=start_date, published_at__lte=end_date) # Moon moon_phase = parse_horoscope() # New actions subscription_comments = Comment.visible_objects()\ .filter( post__subscriptions__user=user, **created_at_condition )\ .values("post__type", "post__slug", "post__title", "post__author_id")\ .annotate(count=Count("id"))\ .order_by() replies = Comment.visible_objects()\ .filter( reply_to__author=user, **created_at_condition )\ .values("post__type", "post__slug", "post__title")\ .annotate(count=Count("reply_to_id"))\ .order_by() new_events = [{ "type": "my_post_comment", "post_url": reverse("show_post", kwargs={ "post_type": e["post__type"], "post_slug": e["post__slug"] }), "post_title": e["post__title"], "count": e["count"], } for e in subscription_comments if e["post__author_id"] == user.id] + [{ "type": "subscribed_post_comment", "post_url": reverse("show_post", kwargs={ "post_type": e["post__type"], "post_slug": e["post__slug"] }), "post_title": e["post__title"], "count": e["count"], } for e in subscription_comments if e["post__author_id"] != user.id] + [{ "type": "reply", "post_url": reverse("show_post", kwargs={ "post_type": e["post__type"], "post_slug": e["post__slug"] }), "post_title": e["post__title"], "count": e["count"], } for e in replies] upvotes = PostVote.objects.filter(post__author=user, **created_at_condition).count() \ + CommentVote.objects.filter(comment__author=user, **created_at_condition).count() if upvotes: new_events = [{ "type": "upvotes", "count": upvotes, }] + new_events # Mentions mentions = Comment.visible_objects() \ .filter(**created_at_condition) \ .filter(text__regex=fr"@\y{user.slug}\y", is_deleted=False)\ .exclude(reply_to__author=user)\ .order_by("-upvotes")[:5] # Best posts posts = Post.visible_objects()\ .filter(is_approved_by_moderator=True, **published_at_condition)\ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST])\ .exclude(is_shadow_banned=True)\ .order_by("-upvotes")[:100] # New joiners intros = Post.visible_objects()\ .filter(type=Post.TYPE_INTRO, **published_at_condition)\ .order_by("-upvotes") if not posts and not mentions and not intros: raise Http404() return render( request, "emails/daily.html", { "user": user, "events": new_events, "intros": intros, "posts": posts, "mentions": mentions, "date": end_date, "moon_phase": moon_phase, })
def items(self): return Post.visible_objects()\ .filter(is_approved_by_moderator=True)\ .exclude(type=Post.TYPE_INTRO)\ .order_by("-published_at", "-created_at")[:self.limit]
def generate_daily_digest(user): end_date = datetime.utcnow() start_date = end_date - timedelta(days=1) if end_date.weekday() == 1: # we don't have daily on sundays and mondays, we need to include all these posts at tuesday start_date = end_date - timedelta(days=3) if settings.DEBUG: start_date = end_date - timedelta(days=1000) created_at_condition = dict(created_at__gte=start_date, created_at__lte=end_date) published_at_condition = dict(published_at__gte=start_date, published_at__lte=end_date) # Moon moon_phase = parse_horoscope() # New actions subscription_comments = Comment.visible_objects()\ .filter( post__subscriptions__user=user, **created_at_condition )\ .values("post__type", "post__slug", "post__title", "post__author_id")\ .annotate(count=Count("id"))\ .order_by() replies = Comment.visible_objects()\ .filter( reply_to__author=user, **created_at_condition )\ .values("post__type", "post__slug", "post__title")\ .annotate(count=Count("reply_to_id"))\ .order_by() new_events = [ { "type": "my_post_comment", "post_url": reverse("show_post", kwargs={"post_type": e["post__type"], "post_slug": e["post__slug"]}), "post_title": e["post__title"], "count": e["count"], } for e in subscription_comments if e["post__author_id"] == user.id ] + [ { "type": "subscribed_post_comment", "post_url": reverse("show_post", kwargs={"post_type": e["post__type"], "post_slug": e["post__slug"]}), "post_title": e["post__title"], "count": e["count"], } for e in subscription_comments if e["post__author_id"] != user.id ] + [ { "type": "reply", "post_url": reverse("show_post", kwargs={"post_type": e["post__type"], "post_slug": e["post__slug"]}), "post_title": e["post__title"], "count": e["count"], } for e in replies ] upvotes = PostVote.objects.filter(post__author=user, **created_at_condition).count() \ + CommentVote.objects.filter(comment__author=user, **created_at_condition).count() if upvotes: new_events = [ { "type": "upvotes", "count": upvotes, } ] + new_events # Mentions mentions = Comment.visible_objects() \ .filter(**created_at_condition) \ .filter(text__regex=fr"@\y{user.slug}\y", is_deleted=False)\ .exclude(reply_to__author=user)\ .order_by("-upvotes")[:5] # Best posts posts = Post.visible_objects()\ .filter(**published_at_condition)\ .filter(Q(is_approved_by_moderator=True) | Q(upvotes__gte=settings.COMMUNITY_APPROVE_UPVOTES))\ .filter(is_visible_in_feeds=True)\ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST])\ .exclude(is_shadow_banned=True)\ .order_by("-upvotes")[:100] # New joiners intros = Post.visible_objects()\ .filter(type=Post.TYPE_INTRO, **published_at_condition)\ .order_by("-upvotes") if not posts and not mentions and not intros: raise NotFound() og_params = urlencode({ **settings.OG_IMAGE_GENERATOR_DEFAULTS, "title": f"Ежедневный дайджест пользователя {user.slug}", "author": "THE MACHINE", "ava": settings.OG_MACHINE_AUTHOR_LOGO }) return render_to_string("emails/daily.html", { "user": user, "events": new_events, "intros": intros, "posts": posts, "mentions": mentions, "date": end_date, "moon_phase": moon_phase, "og_image_url": f"{settings.OG_IMAGE_GENERATOR_URL}?{og_params}" })
def generate_weekly_digest(no_footer=False): end_date = datetime.utcnow() start_date = end_date - timedelta(days=8) if settings.DEBUG: start_date = end_date - timedelta(days=1000) created_at_condition = dict(created_at__gte=start_date, created_at__lte=end_date) published_at_condition = dict(published_at__gte=start_date, published_at__lte=end_date) # New users intros = Post.visible_objects()\ .filter(type=Post.TYPE_INTRO, **published_at_condition)\ .order_by("-upvotes") newbie_count = User.objects\ .filter( moderation_status=User.MODERATION_STATUS_APPROVED, **created_at_condition )\ .count() # Best posts featured_post = Post.visible_objects()\ .exclude(type=Post.TYPE_INTRO)\ .filter( label_code__isnull=False, label_code="top_week", **published_at_condition )\ .order_by("-upvotes")\ .first() posts = Post.visible_objects()\ .filter(**published_at_condition)\ .filter(Q(is_approved_by_moderator=True) | Q(upvotes__gte=settings.COMMUNITY_APPROVE_UPVOTES))\ .filter(is_visible_in_feeds=True)\ .exclude(type__in=[Post.TYPE_INTRO, Post.TYPE_WEEKLY_DIGEST])\ .exclude(id=featured_post.id if featured_post else None)\ .exclude(label_code__isnull=False, label_code="ad")\ .exclude(is_shadow_banned=True)\ .order_by("-upvotes") post_count = posts.count() posts = posts[:12] # Video of the week top_video_comment = Comment.visible_objects()\ .filter(**created_at_condition)\ .filter(is_deleted=False)\ .filter(upvotes__gte=3)\ .filter(Q(text__contains="https://youtu.be/") | Q(text__contains="youtube.com/watch"))\ .order_by("-upvotes")\ .first() top_video_post = None if not top_video_comment: top_video_post = Post.visible_objects() \ .filter(type=Post.TYPE_LINK, upvotes__gte=3) \ .filter(**published_at_condition) \ .filter(Q(url__contains="https://youtu.be/") | Q(url__contains="youtube.com/watch")) \ .order_by("-upvotes") \ .first() # Best comments comments = Comment.visible_objects() \ .filter(**created_at_condition) \ .filter(is_deleted=False)\ .exclude(post__type=Post.TYPE_BATTLE)\ .exclude(post__is_visible=False)\ .exclude(post__is_visible_in_feeds=False)\ .exclude(id=top_video_comment.id if top_video_comment else None)\ .order_by("-upvotes")[:3] # Get intro and title god_settings = GodSettings.objects.first() digest_title = god_settings.digest_title digest_intro = god_settings.digest_intro if not digest_intro and not posts and not comments: raise NotFound() # New achievements achievements = UserAchievement.objects\ .filter(**created_at_condition)\ .select_related("user", "achievement")\ .order_by("achievement") # required for grouping issue_number = (end_date - settings.LAUNCH_DATE).days // 7 og_params = urlencode({ **settings.OG_IMAGE_GENERATOR_DEFAULTS, "title": f"Клубный журнал. Итоги недели. Выпуск #{issue_number}.", "author": "THE MACHINE", "ava": settings.OG_MACHINE_AUTHOR_LOGO }) return render_to_string("emails/weekly.html", { "posts": posts, "comments": comments, "intros": intros, "achievements": achievements, "newbie_count": newbie_count, "post_count": post_count, "top_video_comment": top_video_comment, "top_video_post": top_video_post, "featured_post": featured_post, "digest_title": digest_title, "digest_intro": digest_intro, "issue_number": issue_number, "is_footer_excluded": no_footer, "og_image_url": f"{settings.OG_IMAGE_GENERATOR_URL}?{og_params}" })
def create_links_from_text(cls, post_from, text): links = CLUB_POST_URL_RE.findall(text) for post_slug in links: post_to = Post.visible_objects().filter(slug=post_slug).first() if post_to: cls.link(post_from.author, post_from, post_to)
def feed(request, post_type=POST_TYPE_ALL, topic_slug=None, label_code=None, ordering=ORDERING_ACTIVITY): post_type = post_type or Post if request.me: request.me.update_last_activity() posts = Post.objects_for_user(request.me) else: posts = Post.visible_objects() # filter posts by type if post_type != POST_TYPE_ALL: posts = posts.filter(type=post_type) # filter by topic topic = None if topic_slug: topic = get_object_or_404(Topic, slug=topic_slug) posts = posts.filter(topic=topic) # filter by label if label_code: posts = posts.filter(label_code=label_code) # hide muted users if request.me: muted = Muted.objects.filter(user_from=request.me).values_list("user_to_id").all() if muted: posts = posts.exclude(author_id__in=muted) # hide non-public posts and intros from unauthorized users if not request.me: posts = posts.exclude(is_public=False).exclude(type=Post.TYPE_INTRO) # exclude shadow banned posts, but show them in "new" tab if ordering != ORDERING_NEW: if request.me: posts = posts.exclude(Q(is_shadow_banned=True) & ~Q(author_id=request.me.id)) else: posts = posts.exclude(is_shadow_banned=True) # hide no-feed posts (show only inside rooms and topics) if not topic and not label_code: posts = posts.filter(is_visible_in_feeds=True) # order posts by some metric if ordering: if ordering == ORDERING_ACTIVITY: posts = posts.order_by("-last_activity_at") elif ordering == ORDERING_NEW: posts = posts.order_by("-published_at", "-created_at") elif ordering == ORDERING_TOP: posts = posts.order_by("-upvotes") elif ordering == ORDERING_HOT: posts = posts.order_by("-hotness") elif ordering == ORDERING_TOP_WEEK: posts = posts.filter( published_at__gte=datetime.utcnow() - timedelta(days=7) ).order_by("-upvotes") elif ordering == ORDERING_TOP_MONTH: posts = posts.filter( published_at__gte=datetime.utcnow() - timedelta(days=31) ).order_by("-upvotes") elif ordering == ORDERING_TOP_YEAR: posts = posts.filter( published_at__gte=datetime.utcnow() - timedelta(days=365) ).order_by("-upvotes") else: raise Http404() # split results into pinned and unpinned posts on main page pinned_posts = [] if ordering == ORDERING_ACTIVITY: pinned_posts = posts.filter(is_pinned_until__gte=datetime.utcnow()) posts = posts.exclude(id__in=[p.id for p in pinned_posts]) return render(request, "feed.html", { "post_type": post_type or POST_TYPE_ALL, "ordering": ordering, "topic": topic, "label_code": label_code, "posts": paginate(request, posts), "pinned_posts": pinned_posts, "date_month_ago": datetime.utcnow() - timedelta(days=30), })
def items(self): return Post.visible_objects().filter(is_public=True)
def feed(request, post_type=POST_TYPE_ALL, topic_slug=None, ordering=ORDERING_ACTIVITY): post_type = post_type or Post if request.me: request.me.update_last_activity() posts = Post.objects_for_user(request.me) else: posts = Post.visible_objects() # filter posts by type if post_type != POST_TYPE_ALL: posts = posts.filter(type=post_type) # filter by topic topic = None if topic_slug: topic = get_object_or_404(Topic, slug=topic_slug) posts = posts.filter(topic=topic) # hide non-public posts and intros from unauthorized users if not request.me: posts = posts.exclude(is_public=False).exclude(type=Post.TYPE_INTRO) # exclude shadow banned posts, but show them in "new" tab if ordering != ORDERING_NEW: if request.me: posts = posts.exclude( Q(is_shadow_banned=True) & ~Q(author_id=request.me.id)) else: posts = posts.exclude(is_shadow_banned=True) # no type and topic? probably it's the main page, let's apply some more filters if not topic and post_type == POST_TYPE_ALL: posts = posts.filter(is_visible_on_main_page=True) # order posts by some metric if ordering: if ordering == ORDERING_ACTIVITY: posts = posts.order_by("-last_activity_at") elif ordering == ORDERING_NEW: posts = posts.order_by("-published_at", "-created_at") elif ordering == ORDERING_TOP: posts = posts.order_by("-upvotes") elif ordering == ORDERING_TOP_WEEK: posts = posts.filter(published_at__gte=datetime.utcnow() - timedelta(days=7)).order_by("-upvotes") elif ordering == ORDERING_TOP_MONTH: posts = posts.filter(published_at__gte=datetime.utcnow() - timedelta(days=31)).order_by("-upvotes") else: raise Http404() # split results into pinned and unpinned posts on main page pinned_posts = [] if ordering == ORDERING_ACTIVITY: pinned_posts = posts.filter(is_pinned_until__gte=datetime.utcnow()) posts = posts.exclude(id__in=[p.id for p in pinned_posts]) return render( request, "feed.html", { "post_type": post_type or POST_TYPE_ALL, "ordering": ordering, "topic": topic, "posts": paginate(request, posts), "pinned_posts": pinned_posts, })