feat(carousel): wire is_featured flag to consumer featured events API

ConsumerFeaturedEventsView now includes events with is_featured=True
alongside ad placement results. Placement events retain priority;
is_featured events are appended, deduped, and capped at 10 total.
This commit is contained in:
2026-04-06 17:25:19 +05:30
parent b8a69ceae2
commit 05770d6d21

View File

@@ -9,6 +9,7 @@ import math
from datetime import datetime from datetime import datetime
from django.utils import timezone from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.db import models as db_models from django.db import models as db_models
from django.db.models import Q, Count, Max from django.db.models import Q, Count, Max
from rest_framework.views import APIView from rest_framework.views import APIView
@@ -58,7 +59,7 @@ def _serialize_picker_event(e):
'endDate': str(e.end_date) if e.end_date else '', 'endDate': str(e.end_date) if e.end_date else '',
'organizer': e.partner.name if e.partner else 'Eventify', 'organizer': e.partner.name if e.partner else 'Eventify',
'organizerLogo': '', 'organizerLogo': '',
'category': e.event_type.name if e.event_type else '', 'category': e.event_type.event_type if e.event_type else '',
'coverImage': cover, 'coverImage': cover,
'approvalStatus': 'APPROVED' if e.event_status == 'published' else ( 'approvalStatus': 'APPROVED' if e.event_status == 'published' else (
'REJECTED' if e.event_status == 'cancelled' else 'PENDING' 'REJECTED' if e.event_status == 'cancelled' else 'PENDING'
@@ -212,8 +213,8 @@ class PlacementListCreateView(APIView):
priority=priority, priority=priority,
scope=scope, scope=scope,
rank=max_rank + 1, rank=max_rank + 1,
start_at=datetime.fromisoformat(start_at) if start_at else None, start_at=parse_datetime(start_at) if start_at else None,
end_at=datetime.fromisoformat(end_at) if end_at else None, end_at=parse_datetime(end_at) if end_at else None,
boost_label=boost_label, boost_label=boost_label,
notes=notes, notes=notes,
created_by=request.user, created_by=request.user,
@@ -243,9 +244,9 @@ class PlacementDetailView(APIView):
return JsonResponse({'success': False, 'message': 'Placement not found'}, status=404) return JsonResponse({'success': False, 'message': 'Placement not found'}, status=404)
if 'startAt' in data: if 'startAt' in data:
placement.start_at = datetime.fromisoformat(data['startAt']) if data['startAt'] else None placement.start_at = parse_datetime(data['startAt']) if data['startAt'] else None
if 'endAt' in data: if 'endAt' in data:
placement.end_at = datetime.fromisoformat(data['endAt']) if data['endAt'] else None placement.end_at = parse_datetime(data['endAt']) if data['endAt'] else None
if 'scope' in data: if 'scope' in data:
placement.scope = data['scope'] placement.scope = data['scope']
if 'priority' in data: if 'priority' in data:
@@ -507,8 +508,27 @@ class ConsumerFeaturedEventsView(APIView):
user_lng=user_lng, user_lng=user_lng,
) )
# IDs already covered by ad placements (used for dedup)
placement_ids = {p.event_id for p in placements}
# Start with placement events (they take priority)
events = [_serialize_event_for_consumer(p.event) for p in placements] events = [_serialize_event_for_consumer(p.event) for p in placements]
# Append is_featured events that aren't already in the placement set
featured_qs = (
Event.objects
.filter(is_featured=True, event_status='published')
.exclude(id__in=placement_ids)
.order_by('-start_date', '-created_date')
)
for evt in featured_qs:
if len(events) >= 10:
break
events.append(_serialize_event_for_consumer(evt))
# Cap at 10 total
events = events[:10]
return JsonResponse({'status': 'success', 'events': events}) return JsonResponse({'status': 'success', 'events': events})
except Exception as e: except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500) return JsonResponse({'status': 'error', 'message': str(e)}, status=500)