Phase 7: Reviews Moderation — Review model + migration + 4 admin endpoints (metrics, list, moderate, delete)
This commit is contained in:
@@ -841,3 +841,122 @@ class SettlementReleaseView(APIView):
|
||||
p.payment_transaction_status = 'completed'
|
||||
p.save(update_fields=['payment_transaction_status'])
|
||||
return Response(_serialize_settlement(p))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Phase 7: Reviews Moderation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _reviewer_rank(total_reviews):
|
||||
if total_reviews >= 41:
|
||||
return 'Legend'
|
||||
if total_reviews >= 21:
|
||||
return 'Champion'
|
||||
if total_reviews >= 11:
|
||||
return 'Enthusiast'
|
||||
if total_reviews >= 4:
|
||||
return 'Contributor'
|
||||
return 'Explorer'
|
||||
|
||||
|
||||
def _serialize_review(r):
|
||||
from admin_api.models import Review
|
||||
try:
|
||||
name = r.reviewer.get_full_name() or r.reviewer.username
|
||||
email = r.reviewer.email
|
||||
total = Review.objects.filter(reviewer=r.reviewer, status='live').count()
|
||||
except Exception:
|
||||
name, email, total = '', '', 0
|
||||
try:
|
||||
event_name = getattr(r.event, 'title', None) or getattr(r.event, 'name', '') or ''
|
||||
event_id = str(r.event.id)
|
||||
start = getattr(r.event, 'start_date', None)
|
||||
event_date = start.isoformat() if start else ''
|
||||
except Exception:
|
||||
event_name, event_id, event_date = '', '', ''
|
||||
return {
|
||||
'id': str(r.id),
|
||||
'reviewerName': name,
|
||||
'reviewerEmail': email,
|
||||
'reviewerAvatar': '',
|
||||
'eventName': event_name,
|
||||
'eventId': event_id,
|
||||
'eventDate': event_date,
|
||||
'rating': r.rating,
|
||||
'reviewText': r.review_text,
|
||||
'submissionDate': r.submission_date.isoformat(),
|
||||
'status': r.status,
|
||||
'reviewerRank': _reviewer_rank(total),
|
||||
'reviewerTotalReviews': total,
|
||||
'rejectReason': r.reject_reason or '',
|
||||
}
|
||||
|
||||
|
||||
class ReviewMetricsView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
from admin_api.models import Review
|
||||
return Response({
|
||||
'totalPending': Review.objects.filter(status='pending').count(),
|
||||
'liveReviews': Review.objects.filter(status='live').count(),
|
||||
'rejected': Review.objects.filter(status='rejected').count(),
|
||||
})
|
||||
|
||||
|
||||
class ReviewListView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
from admin_api.models import Review
|
||||
qs = Review.objects.select_related('reviewer', 'event').order_by('-submission_date')
|
||||
status = request.GET.get('status')
|
||||
if status in ('pending', 'live', 'rejected'):
|
||||
qs = qs.filter(status=status)
|
||||
try:
|
||||
page = max(1, int(request.GET.get('page', 1)))
|
||||
page_size = min(100, int(request.GET.get('page_size', 20)))
|
||||
except (ValueError, TypeError):
|
||||
page, page_size = 1, 20
|
||||
total = qs.count()
|
||||
reviews = qs[(page - 1) * page_size: page * page_size]
|
||||
return Response({'count': total, 'results': [_serialize_review(r) for r in reviews]})
|
||||
|
||||
|
||||
class ReviewModerationView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def patch(self, request, pk):
|
||||
from admin_api.models import Review
|
||||
from django.shortcuts import get_object_or_404
|
||||
review = get_object_or_404(Review, pk=pk)
|
||||
action = request.data.get('action')
|
||||
if action == 'approve':
|
||||
review.status = 'live'
|
||||
elif action == 'reject':
|
||||
rr = request.data.get('reject_reason', 'spam')
|
||||
valid_reasons = [c[0] for c in Review.REJECT_CHOICES]
|
||||
if rr not in valid_reasons:
|
||||
return Response({'error': 'Invalid reject_reason'}, status=400)
|
||||
review.status = 'rejected'
|
||||
review.reject_reason = rr
|
||||
elif action == 'save_and_approve':
|
||||
review.review_text = request.data.get('review_text', review.review_text)
|
||||
review.status = 'live'
|
||||
elif action == 'save_live':
|
||||
review.review_text = request.data.get('review_text', review.review_text)
|
||||
else:
|
||||
return Response({'error': 'Invalid action'}, status=400)
|
||||
review.save()
|
||||
return Response(_serialize_review(review))
|
||||
|
||||
|
||||
class ReviewDeleteView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def delete(self, request, pk):
|
||||
from admin_api.models import Review
|
||||
from django.shortcuts import get_object_or_404
|
||||
review = get_object_or_404(Review, pk=pk)
|
||||
review.delete()
|
||||
return Response(status=204)
|
||||
|
||||
Reference in New Issue
Block a user