feat: add community contribution ranks
This commit is contained in:
@@ -100,23 +100,41 @@ class PostAdmin(BaseModelAdmin, ImportExportModelAdmin):
|
||||
actions = BaseModelAdmin.actions + ['submit_for_review', 'request_changes', 'make_published', 'make_draft', 'make_featured', 'restore_posts']
|
||||
|
||||
def submit_for_review(self, request, queryset):
|
||||
queryset.update(status='submitted')
|
||||
self.message_user(request, f"Submitted {queryset.count()} posts for review.")
|
||||
count = 0
|
||||
for post in queryset:
|
||||
post.status = Post.StatusChoices.SUBMITTED
|
||||
post.save(update_fields=["status", "updated_at"])
|
||||
count += 1
|
||||
self.message_user(request, f"Submitted {count} posts for review.")
|
||||
submit_for_review.short_description = "Submit selected posts for review"
|
||||
|
||||
def request_changes(self, request, queryset):
|
||||
queryset.update(status='changes_requested', reviewed_by=request.user)
|
||||
self.message_user(request, f"Requested changes on {queryset.count()} posts.")
|
||||
count = 0
|
||||
for post in queryset:
|
||||
post.status = Post.StatusChoices.CHANGES_REQUESTED
|
||||
post.reviewed_by = request.user
|
||||
post.save(update_fields=["status", "reviewed_by", "updated_at"])
|
||||
count += 1
|
||||
self.message_user(request, f"Requested changes on {count} posts.")
|
||||
request_changes.short_description = "Request changes on selected posts"
|
||||
|
||||
def make_published(self, request, queryset):
|
||||
queryset.update(status='published', published_by=request.user)
|
||||
self.message_user(request, f"Published {queryset.count()} posts.")
|
||||
count = 0
|
||||
for post in queryset:
|
||||
post.status = Post.StatusChoices.PUBLISHED
|
||||
post.published_by = request.user
|
||||
post.save(update_fields=["status", "published_by", "published_at", "updated_at"])
|
||||
count += 1
|
||||
self.message_user(request, f"Published {count} posts.")
|
||||
make_published.short_description = "Mark selected posts as published"
|
||||
|
||||
def make_draft(self, request, queryset):
|
||||
queryset.update(status='draft')
|
||||
self.message_user(request, f"Marked {queryset.count()} posts as draft.")
|
||||
count = 0
|
||||
for post in queryset:
|
||||
post.status = Post.StatusChoices.DRAFT
|
||||
post.save(update_fields=["status", "updated_at"])
|
||||
count += 1
|
||||
self.message_user(request, f"Marked {count} posts as draft.")
|
||||
make_draft.short_description = "Mark selected posts as draft"
|
||||
|
||||
def make_featured(self, request, queryset):
|
||||
@@ -156,8 +174,19 @@ class CommentAdmin(BaseModelAdmin):
|
||||
content_preview.short_description = 'Content Preview'
|
||||
|
||||
def approve_comments(self, request, queryset):
|
||||
queryset.update(is_approved=True, is_hidden=False, hidden_by=None, hidden_at=None, moderation_note='')
|
||||
self.message_user(request, f"Approved {queryset.count()} comments.")
|
||||
count = 0
|
||||
for comment in queryset:
|
||||
comment.is_approved = True
|
||||
comment.is_hidden = False
|
||||
comment.hidden_by = None
|
||||
comment.hidden_at = None
|
||||
comment.moderation_note = ""
|
||||
comment.save(update_fields=[
|
||||
"is_approved", "is_hidden", "hidden_by", "hidden_at",
|
||||
"moderation_note", "updated_at",
|
||||
])
|
||||
count += 1
|
||||
self.message_user(request, f"Approved {count} comments.")
|
||||
approve_comments.short_description = "Approve selected comments"
|
||||
|
||||
def hide_comments(self, request, queryset):
|
||||
|
||||
@@ -356,6 +356,22 @@ class BlogProfileActivitySchema(Schema):
|
||||
replies: List[CommentSchema]
|
||||
|
||||
|
||||
class PaginatedProfilePostsSchema(Schema):
|
||||
count: int
|
||||
page: int
|
||||
page_size: int
|
||||
total_pages: int
|
||||
results: List[PostListSchema]
|
||||
|
||||
|
||||
class PaginatedProfileCommentsSchema(Schema):
|
||||
count: int
|
||||
page: int
|
||||
page_size: int
|
||||
total_pages: int
|
||||
results: List[CommentSchema]
|
||||
|
||||
|
||||
class BlogBannerSchema(ModelSchema):
|
||||
image_url: str
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ from apps.blog.api.schemas import (
|
||||
BlogFiltersSchema,
|
||||
BlogInteractionSchema,
|
||||
BlogProfileActivitySchema,
|
||||
PaginatedProfileCommentsSchema,
|
||||
PaginatedProfilePostsSchema,
|
||||
CategorySchema,
|
||||
CategoryWriteSchema,
|
||||
CommentCreateSchema,
|
||||
@@ -608,6 +610,48 @@ def my_blog_activity(request):
|
||||
}
|
||||
|
||||
|
||||
def _profile_page_payload(queryset, page: int, page_size: int = 10):
|
||||
count = queryset.count()
|
||||
total_pages = max(1, (count + page_size - 1) // page_size)
|
||||
start = (page - 1) * page_size
|
||||
return {
|
||||
"count": count,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_pages": total_pages,
|
||||
"results": list(queryset[start : start + page_size]),
|
||||
}
|
||||
|
||||
|
||||
@blog_router.get("/me/activity/liked", response=PaginatedProfilePostsSchema, auth=jwt_auth)
|
||||
def my_liked_posts(request, page: int = Query(1, ge=1)):
|
||||
queryset = _published_queryset().filter(likes__user=request.auth).order_by("-published_at", "-created_at", "-id")
|
||||
return _profile_page_payload(queryset, page)
|
||||
|
||||
|
||||
@blog_router.get("/me/activity/saved", response=PaginatedProfilePostsSchema, auth=jwt_auth)
|
||||
def my_saved_posts(request, page: int = Query(1, ge=1)):
|
||||
queryset = _published_queryset().filter(saves__user=request.auth).order_by("-published_at", "-created_at", "-id")
|
||||
return _profile_page_payload(queryset, page)
|
||||
|
||||
|
||||
@blog_router.get("/me/activity/comments", response=PaginatedProfileCommentsSchema, auth=jwt_auth)
|
||||
def my_blog_comments(request, page: int = Query(1, ge=1)):
|
||||
queryset = (
|
||||
Comment.objects.filter(
|
||||
author=request.auth,
|
||||
is_approved=True,
|
||||
is_hidden=False,
|
||||
is_deleted=False,
|
||||
post__status=Post.StatusChoices.PUBLISHED,
|
||||
post__is_deleted=False,
|
||||
)
|
||||
.select_related("author", "post")
|
||||
.order_by("-created_at", "-id")
|
||||
)
|
||||
return _profile_page_payload(queryset, page)
|
||||
|
||||
|
||||
@blog_router.get("/banners", response=List[BlogBannerSchema])
|
||||
def list_banners(request):
|
||||
return BlogBanner.objects.filter(is_active=True, is_deleted=False).order_by("sort_order", "-created_at")
|
||||
|
||||
@@ -374,6 +374,9 @@ class Comment(BaseModel):
|
||||
moderation_note=note,
|
||||
updated_at=now,
|
||||
)
|
||||
from apps.gamification.services import sync_comment
|
||||
for comment in self.__class__.all_objects.filter(id__in=ids).select_related("author", "post"):
|
||||
sync_comment(comment)
|
||||
|
||||
def unhide(self):
|
||||
now = timezone.now()
|
||||
@@ -386,6 +389,9 @@ class Comment(BaseModel):
|
||||
moderation_note="",
|
||||
updated_at=now,
|
||||
)
|
||||
from apps.gamification.services import sync_comment
|
||||
for comment in self.__class__.all_objects.filter(id__in=ids).select_related("author", "post"):
|
||||
sync_comment(comment)
|
||||
|
||||
def descendant_ids(self) -> list[int]:
|
||||
pending = [self.id]
|
||||
@@ -408,6 +414,9 @@ class Comment(BaseModel):
|
||||
delete_note=note,
|
||||
updated_at=now,
|
||||
)
|
||||
from apps.gamification.services import sync_comment
|
||||
for comment in self.__class__.all_objects.filter(id__in=ids).select_related("author", "post"):
|
||||
sync_comment(comment)
|
||||
|
||||
|
||||
class Like(models.Model):
|
||||
|
||||
@@ -89,6 +89,33 @@ def test_blog_interactions_and_activity(client, blog_content, user_factory, auth
|
||||
assert not Like.objects.filter(post=post, user=member).exists()
|
||||
|
||||
|
||||
def test_my_blog_activity_paginates_each_category_independently(client, blog_content, user_factory, auth_headers):
|
||||
_, _, _, _, _, _, _ = blog_content
|
||||
member = user_factory()
|
||||
headers = auth_headers(member)
|
||||
posts = [
|
||||
Post.objects.create(title=f"Saved and liked {index}", content="Content", author=member,
|
||||
status=Post.StatusChoices.PUBLISHED)
|
||||
for index in range(12)
|
||||
]
|
||||
for post in posts:
|
||||
Like.objects.create(post=post, user=member)
|
||||
SavedPost.objects.create(post=post, user=member)
|
||||
Comment.objects.create(post=post, author=member, content=f"Comment {post.id}")
|
||||
|
||||
liked_page = client.get("/api/blog/me/activity/liked", {"page": 1}, **headers)
|
||||
saved_page = client.get("/api/blog/me/activity/saved", {"page": 2}, **headers)
|
||||
comments_page = client.get("/api/blog/me/activity/comments", {"page": 2}, **headers)
|
||||
|
||||
assert liked_page.status_code == 200, liked_page.content
|
||||
assert liked_page.json()["count"] == 12
|
||||
assert liked_page.json()["page_size"] == 10
|
||||
assert len(liked_page.json()["results"]) == 10
|
||||
assert len(saved_page.json()["results"]) == 2
|
||||
assert len(comments_page.json()["results"]) == 2
|
||||
assert comments_page.json()["total_pages"] == 2
|
||||
|
||||
|
||||
def test_blog_editor_review_and_moderation(client, blog_content, user_factory, auth_headers, mocker):
|
||||
author, _, _, _, _, _, _ = blog_content
|
||||
editor = user_factory(is_staff=True)
|
||||
|
||||
Reference in New Issue
Block a user