Files
ShooterHub/apps/social/views.py
2026-04-02 11:24:30 +02:00

263 lines
9.8 KiB
Python

from django.contrib.auth import get_user_model
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status, viewsets
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import BlogPost, Bug, BugStatus, Friendship, FriendshipStatus, Message
from .serializers import (
BlogPostSerializer,
BugSerializer,
FriendRequestSerializer,
FriendshipSerializer,
MessageCreateSerializer,
MessageDetailSerializer,
MessageListSerializer,
)
User = get_user_model()
# ── Member search (for adding friends) ───────────────────────────────────────
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def member_search(request):
q = request.query_params.get('q', '').strip()
if len(q) < 2:
return Response([])
users = (
User.objects
.exclude(pk=request.user.pk)
.filter(
Q(username__icontains=q) |
Q(first_name__icontains=q) |
Q(last_name__icontains=q)
)[:20]
)
data = [
{
'id': u.id,
'username': u.username,
'display_name': (f'{u.first_name} {u.last_name}'.strip()) or u.username,
}
for u in users
]
return Response(data)
# ── Messages ──────────────────────────────────────────────────────────────────
class MessageViewSet(viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
def list(self, request):
"""Inbox: messages received by the current user."""
qs = (
Message.objects
.filter(recipient=request.user, deleted_by_recipient=False)
.select_related('sender', 'recipient')
.order_by('-sent_at')
)
return Response(MessageListSerializer(qs, many=True).data)
def create(self, request):
ser = MessageCreateSerializer(data=request.data, context={'request': request})
ser.is_valid(raise_exception=True)
msg = ser.save(sender=request.user)
return Response(MessageDetailSerializer(msg).data, status=status.HTTP_201_CREATED)
def retrieve(self, request, pk=None):
user = request.user
msg = get_object_or_404(
Message,
Q(sender=user, deleted_by_sender=False) |
Q(recipient=user, deleted_by_recipient=False),
pk=pk,
)
if msg.recipient == user and msg.read_at is None:
msg.read_at = timezone.now()
msg.save(update_fields=['read_at'])
return Response(MessageDetailSerializer(msg).data)
def destroy(self, request, pk=None):
user = request.user
msg = get_object_or_404(
Message,
Q(sender=user) | Q(recipient=user),
pk=pk,
)
if msg.sender == user:
msg.deleted_by_sender = True
if msg.recipient == user:
msg.deleted_by_recipient = True
msg.save(update_fields=['deleted_by_sender', 'deleted_by_recipient'])
return Response(status=status.HTTP_204_NO_CONTENT)
@action(detail=False, methods=['get'], url_path='sent')
def sent(self, request):
qs = (
Message.objects
.filter(sender=request.user, deleted_by_sender=False)
.select_related('sender', 'recipient')
.order_by('-sent_at')
)
return Response(MessageListSerializer(qs, many=True).data)
@action(detail=False, methods=['get'], url_path='unread-count')
def unread_count(self, request):
count = Message.objects.filter(
recipient=request.user, read_at__isnull=True, deleted_by_recipient=False
).count()
return Response({'unread': count})
# ── BlogPost ──────────────────────────────────────────────────────────────────
class BlogPostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = BlogPostSerializer
search_fields = ['title', 'body']
ordering_fields = ['created_at']
def get_queryset(self):
user = self.request.user
return (
BlogPost.objects
.filter(Q(author=user) | Q(is_public=True))
.select_related('author')
)
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def check_write_permission(self, instance):
from rest_framework.exceptions import PermissionDenied
if instance.author != self.request.user and not self.request.user.is_staff:
raise PermissionDenied
def perform_update(self, serializer):
self.check_write_permission(self.get_object())
serializer.save()
def perform_destroy(self, instance):
self.check_write_permission(instance)
instance.delete()
# ── Bug ───────────────────────────────────────────────────────────────────────
class BugViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = BugSerializer
filterset_fields = ['severity', 'status']
search_fields = ['title', 'description']
ordering_fields = ['created_at', 'severity']
def get_queryset(self):
user = self.request.user
if user.is_staff:
return Bug.objects.select_related('reporter').all()
return Bug.objects.filter(reporter=user).select_related('reporter')
def perform_create(self, serializer):
serializer.save(reporter=self.request.user)
@action(detail=True, methods=['post'], url_path='resolve')
def resolve(self, request, pk=None):
from rest_framework.exceptions import PermissionDenied
if not request.user.is_staff:
raise PermissionDenied
bug = self.get_object()
bug.status = BugStatus.RESOLVED
bug.resolved_at = timezone.now()
bug.save(update_fields=['status', 'resolved_at', 'updated_at'])
return Response(BugSerializer(bug).data)
# ── Friendship ────────────────────────────────────────────────────────────────
class FriendshipViewSet(viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
serializer_class = FriendshipSerializer
def list(self, request):
"""Accepted friends."""
user = request.user
qs = (
Friendship.objects
.filter(Q(from_user=user) | Q(to_user=user), status=FriendshipStatus.ACCEPTED)
.select_related('from_user', 'to_user')
)
return Response(FriendshipSerializer(qs, many=True).data)
def create(self, request):
ser = FriendRequestSerializer(data=request.data, context={'request': request})
ser.is_valid(raise_exception=True)
to_user = ser.validated_data['to_user']
user = request.user
existing = Friendship.objects.filter(
Q(from_user=user, to_user=to_user) |
Q(from_user=to_user, to_user=user)
).first()
if existing:
return Response({'detail': 'A friendship or request already exists.'}, status=status.HTTP_409_CONFLICT)
friendship = Friendship.objects.create(from_user=user, to_user=to_user)
return Response(FriendshipSerializer(friendship).data, status=status.HTTP_201_CREATED)
def destroy(self, request, pk=None):
user = request.user
friendship = get_object_or_404(
Friendship,
Q(from_user=user) | Q(to_user=user),
pk=pk,
)
friendship.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@action(detail=False, methods=['get'], url_path='requests')
def requests(self, request):
"""Incoming pending requests."""
qs = (
Friendship.objects
.filter(to_user=request.user, status=FriendshipStatus.PENDING)
.select_related('from_user', 'to_user')
)
return Response(FriendshipSerializer(qs, many=True).data)
@action(detail=False, methods=['get'], url_path='sent-requests')
def sent_requests(self, request):
"""Outgoing pending requests."""
qs = (
Friendship.objects
.filter(from_user=request.user, status=FriendshipStatus.PENDING)
.select_related('from_user', 'to_user')
)
return Response(FriendshipSerializer(qs, many=True).data)
@action(detail=True, methods=['post'], url_path='accept')
def accept(self, request, pk=None):
friendship = get_object_or_404(
Friendship, pk=pk, to_user=request.user, status=FriendshipStatus.PENDING
)
friendship.status = FriendshipStatus.ACCEPTED
friendship.save(update_fields=['status', 'updated_at'])
return Response(FriendshipSerializer(friendship).data)
@action(detail=True, methods=['post'], url_path='decline')
def decline(self, request, pk=None):
"""Decline an incoming request or cancel an outgoing one."""
user = request.user
friendship = get_object_or_404(
Friendship,
Q(from_user=user) | Q(to_user=user),
pk=pk, status=FriendshipStatus.PENDING,
)
friendship.delete()
return Response(status=status.HTTP_204_NO_CONTENT)