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:
0
notifications/__init__.py
Normal file
0
notifications/__init__.py
Normal file
10
notifications/admin.py
Normal file
10
notifications/admin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
from .models import Notification
|
||||
|
||||
|
||||
@admin.register(Notification)
|
||||
class NotificationAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'user', 'notification_type', 'is_read', 'created_at')
|
||||
list_filter = ('notification_type', 'is_read', 'created_at')
|
||||
search_fields = ('title', 'message', 'user__email')
|
||||
readonly_fields = ('created_at',)
|
||||
6
notifications/apps.py
Normal file
6
notifications/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class NotificationsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'notifications'
|
||||
25
notifications/models.py
Normal file
25
notifications/models.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
from accounts.models import User
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
NOTIFICATION_TYPES = [
|
||||
('event', 'Event'),
|
||||
('promo', 'Promotion'),
|
||||
('system', 'System'),
|
||||
('booking', 'Booking'),
|
||||
]
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications')
|
||||
title = models.CharField(max_length=255)
|
||||
message = models.TextField()
|
||||
notification_type = models.CharField(max_length=20, choices=NOTIFICATION_TYPES, default='system')
|
||||
is_read = models.BooleanField(default=False)
|
||||
action_url = models.URLField(blank=True, null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.notification_type}: {self.title} → {self.user.email}"
|
||||
8
notifications/urls.py
Normal file
8
notifications/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.urls import path
|
||||
from .views import NotificationListView, NotificationMarkReadView, NotificationCountView
|
||||
|
||||
urlpatterns = [
|
||||
path('list/', NotificationListView.as_view(), name='notification_list'),
|
||||
path('mark-read/', NotificationMarkReadView.as_view(), name='notification_mark_read'),
|
||||
path('count/', NotificationCountView.as_view(), name='notification_count'),
|
||||
]
|
||||
85
notifications/views.py
Normal file
85
notifications/views.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import json
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.http import JsonResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from mobile_api.utils import validate_token_and_get_user
|
||||
from eventify_logger.services import log
|
||||
from .models import Notification
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class NotificationListView(View):
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
page = int(data.get('page', 1))
|
||||
page_size = int(data.get('page_size', 20))
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
notifications = Notification.objects.filter(user=user)[offset:offset + page_size]
|
||||
total = Notification.objects.filter(user=user).count()
|
||||
|
||||
items = [{
|
||||
'id': n.id,
|
||||
'title': n.title,
|
||||
'message': n.message,
|
||||
'notification_type': n.notification_type,
|
||||
'is_read': n.is_read,
|
||||
'action_url': n.action_url or '',
|
||||
'created_at': n.created_at.isoformat(),
|
||||
} for n in notifications]
|
||||
|
||||
return JsonResponse({
|
||||
'status': 'success',
|
||||
'notifications': items,
|
||||
'total': total,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
})
|
||||
except Exception as e:
|
||||
log("error", "NotificationListView error", request=request, logger_data={"error": str(e)})
|
||||
return JsonResponse({'error': 'An unexpected server error occurred.'}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class NotificationMarkReadView(View):
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
mark_all = data.get('mark_all', False)
|
||||
notification_id = data.get('notification_id')
|
||||
|
||||
if mark_all:
|
||||
Notification.objects.filter(user=user, is_read=False).update(is_read=True)
|
||||
return JsonResponse({'status': 'success', 'message': 'All notifications marked as read'})
|
||||
|
||||
if notification_id:
|
||||
Notification.objects.filter(id=notification_id, user=user).update(is_read=True)
|
||||
return JsonResponse({'status': 'success', 'message': 'Notification marked as read'})
|
||||
|
||||
return JsonResponse({'error': 'Provide notification_id or mark_all=true'}, status=400)
|
||||
except Exception as e:
|
||||
log("error", "NotificationMarkReadView error", request=request, logger_data={"error": str(e)})
|
||||
return JsonResponse({'error': 'An unexpected server error occurred.'}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class NotificationCountView(View):
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
count = Notification.objects.filter(user=user, is_read=False).count()
|
||||
return JsonResponse({'status': 'success', 'unread_count': count})
|
||||
except Exception as e:
|
||||
log("error", "NotificationCountView error", request=request, logger_data={"error": str(e)})
|
||||
return JsonResponse({'error': 'An unexpected server error occurred.'}, status=500)
|
||||
Reference in New Issue
Block a user