feat(favorites): add EventLike model, favorites API, and notifications module
- EventLike model (user × event unique constraint, indexed) - contributed_by field on Event (EVT ID or email of community contributor) - Favorites API endpoints: toggle-like, my-likes, my-liked-events - Notifications app wired into main urls.py at /api/notifications/ - accounts migration 0014_merge_0013 (resolves split 0013 branches) - requirements.txt updated
This commit is contained in:
@@ -2,6 +2,7 @@ from django.urls import path
|
||||
from .views import *
|
||||
from mobile_api.views.user import ScheduleCallView
|
||||
from mobile_api.views.reviews import ReviewSubmitView, MobileReviewListView, ReviewHelpfulView, ReviewFlagView
|
||||
from mobile_api.views.favorites import ToggleLikeView, MyLikedIdsView, MyLikedEventsView
|
||||
from ad_control.views import ConsumerFeaturedEventsView, ConsumerTopEventsView
|
||||
|
||||
|
||||
@@ -39,3 +40,10 @@ urlpatterns += [
|
||||
path('reviews/helpful', ReviewHelpfulView.as_view()),
|
||||
path('reviews/flag', ReviewFlagView.as_view()),
|
||||
]
|
||||
|
||||
# Favorites URLs
|
||||
urlpatterns += [
|
||||
path('events/like/', ToggleLikeView.as_view()),
|
||||
path('events/my-likes/', MyLikedIdsView.as_view()),
|
||||
path('events/my-liked-events/', MyLikedEventsView.as_view()),
|
||||
]
|
||||
|
||||
146
mobile_api/views/favorites.py
Normal file
146
mobile_api/views/favorites.py
Normal file
@@ -0,0 +1,146 @@
|
||||
from django.views import View
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.http import JsonResponse
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from events.models import Event, EventLike, EventImages
|
||||
from mobile_api.utils import validate_token_and_get_user
|
||||
from eventify_logger.services import log
|
||||
|
||||
|
||||
def _serialize_liked_event(event):
|
||||
"""Serialize an Event for the liked-events list."""
|
||||
primary_img = EventImages.objects.filter(
|
||||
event=event, is_primary=True
|
||||
).first()
|
||||
if not primary_img:
|
||||
primary_img = EventImages.objects.filter(event=event).first()
|
||||
|
||||
return {
|
||||
'id': event.id,
|
||||
'title': event.title or event.name,
|
||||
'image': primary_img.event_image.url if primary_img else '',
|
||||
'date': str(event.start_date) if event.start_date else None,
|
||||
'location': event.place or '',
|
||||
'venue': event.venue_name or '',
|
||||
'event_type': event.event_type.event_type if event.event_type else '',
|
||||
'event_status': event.event_status,
|
||||
}
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class ToggleLikeView(View):
|
||||
"""POST /api/events/like/ — toggle like on/off for an event."""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
event_id = data.get('event_id')
|
||||
if not event_id:
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'event_id is required'},
|
||||
status=400
|
||||
)
|
||||
|
||||
try:
|
||||
event = Event.objects.get(pk=event_id)
|
||||
except Event.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'Event not found'},
|
||||
status=404
|
||||
)
|
||||
|
||||
like, created = EventLike.objects.get_or_create(user=user, event=event)
|
||||
if not created:
|
||||
like.delete()
|
||||
return JsonResponse({'status': 'success', 'liked': False})
|
||||
|
||||
return JsonResponse({'status': 'success', 'liked': True})
|
||||
|
||||
except Exception as e:
|
||||
log("error", "ToggleLikeView exception", request=request,
|
||||
logger_data={"error": str(e)})
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'An unexpected server error occurred.'},
|
||||
status=500
|
||||
)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class MyLikedIdsView(View):
|
||||
"""POST /api/events/my-likes/ — return all liked event IDs for the user."""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
liked_ids = list(
|
||||
EventLike.objects.filter(user=user)
|
||||
.values_list('event_id', flat=True)
|
||||
)
|
||||
return JsonResponse({'status': 'success', 'liked_event_ids': liked_ids})
|
||||
|
||||
except Exception as e:
|
||||
log("error", "MyLikedIdsView exception", request=request,
|
||||
logger_data={"error": str(e)})
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'An unexpected server error occurred.'},
|
||||
status=500
|
||||
)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class MyLikedEventsView(View):
|
||||
"""POST /api/events/my-liked-events/ — paginated liked events with full data."""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
page = int(data.get('page', 1))
|
||||
page_size = min(int(data.get('page_size', 20)), 50)
|
||||
|
||||
# Event IDs liked by this user, newest first
|
||||
liked_event_ids = list(
|
||||
EventLike.objects.filter(user=user)
|
||||
.order_by('-created_at')
|
||||
.values_list('event_id', flat=True)
|
||||
)
|
||||
|
||||
# Preserve ordering from liked_event_ids
|
||||
from django.db.models import Case, When, IntegerField
|
||||
ordering = Case(
|
||||
*[When(pk=pk, then=pos) for pos, pk in enumerate(liked_event_ids)],
|
||||
output_field=IntegerField()
|
||||
)
|
||||
events_qs = Event.objects.filter(id__in=liked_event_ids).order_by(ordering)
|
||||
|
||||
paginator = Paginator(events_qs, page_size)
|
||||
page_obj = paginator.get_page(page)
|
||||
|
||||
events_data = [_serialize_liked_event(e) for e in page_obj]
|
||||
|
||||
return JsonResponse({
|
||||
'status': 'success',
|
||||
'events': events_data,
|
||||
'total': paginator.count,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'has_next': page_obj.has_next(),
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
log("error", "MyLikedEventsView exception", request=request,
|
||||
logger_data={"error": str(e)})
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'An unexpected server error occurred.'},
|
||||
status=500
|
||||
)
|
||||
Reference in New Issue
Block a user