From aaaab190da32e1b0e86a59450bb4129742cd2bd2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 24 Mar 2026 12:20:34 +0000 Subject: [PATCH] feat: add is_featured/is_top_event fields and API endpoints - Event model: added is_featured, is_top_event BooleanFields - Migration 0007 applied to DB - EventForm: checkboxes for both new fields - EventAdmin: list_display, list_editable, list_filter for both flags - FeaturedEventsAPI: POST /api/events/featured-events/ -> is_featured=True events - TopEventsAPI: POST /api/events/top-events/ -> is_top_event=True events --- events/admin.py | 9 ++-- events/forms.py | 6 ++- .../0007_add_is_featured_is_top_event.py | 21 ++++++++ events/models.py | 3 ++ mobile_api/urls.py | 2 + mobile_api/views/events.py | 54 ++++++++++++++++++- 6 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 events/migrations/0007_add_is_featured_is_top_event.py diff --git a/events/admin.py b/events/admin.py index 1f4fff0..b935de6 100644 --- a/events/admin.py +++ b/events/admin.py @@ -3,10 +3,11 @@ from .models import Event, EventImages @admin.register(Event) class EventAdmin(admin.ModelAdmin): - list_display = ('id','name','start_date','end_date','event_type','event_status') - list_filter = ('event_status','event_type') - search_fields = ('name','place','district') + list_display = ('id', 'name', 'start_date', 'end_date', 'event_type', 'event_status', 'is_featured', 'is_top_event') + list_filter = ('event_status', 'event_type', 'is_featured', 'is_top_event') + list_editable = ('is_featured', 'is_top_event') + search_fields = ('name', 'place', 'district') @admin.register(EventImages) class EventImagesAdmin(admin.ModelAdmin): - list_display = ('id','event','is_primary') + list_display = ('id', 'event', 'is_primary') diff --git a/events/forms.py b/events/forms.py index e38ca7b..6346e7c 100644 --- a/events/forms.py +++ b/events/forms.py @@ -1,7 +1,7 @@ from django import forms from .models import Event from .models import EventImages - +from django_summernote.widgets import SummernoteWidget class EventForm(forms.ModelForm): class Meta: @@ -11,7 +11,7 @@ class EventForm(forms.ModelForm): 'name': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control'}), 'title': forms.TextInput(attrs={'class': 'form-control'}), - 'important_information': forms.Textarea(attrs={'class': 'form-control'}), + 'important_information': SummernoteWidget(attrs={'summernote': {'width': '100%', 'height': '400px'}}), 'venue_name': forms.TextInput(attrs={'class': 'form-control'}), 'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date', 'id': 'id_start_date'}), 'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date', 'id': 'id_end_date'}), @@ -30,6 +30,8 @@ class EventForm(forms.ModelForm): 'cancelled_reason': forms.Textarea(attrs={'class': 'form-control'}), 'is_bookable': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'is_eventify_event': forms.CheckboxInput(attrs={'class': 'form-check-input'}), + 'is_featured': forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'id_is_featured'}), + 'is_top_event': forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'id_is_top_event'}), } def __init__(self, *args, **kwargs): diff --git a/events/migrations/0007_add_is_featured_is_top_event.py b/events/migrations/0007_add_is_featured_is_top_event.py new file mode 100644 index 0000000..9bfc20d --- /dev/null +++ b/events/migrations/0007_add_is_featured_is_top_event.py @@ -0,0 +1,21 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0006_alter_event_source'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='is_featured', + field=models.BooleanField(default=False, help_text='Show this event in the featured section'), + ), + migrations.AddField( + model_name='event', + name='is_top_event', + field=models.BooleanField(default=False, help_text='Show this event in the Top Events section'), + ), + ] diff --git a/events/models.py b/events/models.py index b37cc01..7210c28 100644 --- a/events/models.py +++ b/events/models.py @@ -54,6 +54,9 @@ class Event(models.Model): ('community', 'Community'), ]) + is_featured = models.BooleanField(default=False, help_text='Show this event in the featured section') + is_top_event = models.BooleanField(default=False, help_text='Show this event in the Top Events section') + def __str__(self): return f"{self.name} ({self.start_date})" diff --git a/mobile_api/urls.py b/mobile_api/urls.py index e924727..a9a4cbc 100644 --- a/mobile_api/urls.py +++ b/mobile_api/urls.py @@ -21,4 +21,6 @@ urlpatterns += [ path('events/events-by-category/', EventsByCategoryAPI.as_view(), name='api_events_by_category'), path('events/events-by-month-year/', EventsByMonthYearAPI.as_view(), name='events_by_month_year'), path('events/events-by-date/', EventsByDateAPI.as_view(), name='events_by_date'), + path('events/featured-events/', FeaturedEventsAPI.as_view(), name='featured_events'), + path('events/top-events/', TopEventsAPI.as_view(), name='top_events'), ] diff --git a/mobile_api/views/events.py b/mobile_api/views/events.py index b4ee4bb..27ada07 100644 --- a/mobile_api/views/events.py +++ b/mobile_api/views/events.py @@ -366,4 +366,56 @@ class EventsByDateAPI(APIView): except Exception as e: return JsonResponse( {"status": "error", "message": str(e)}, - ) \ No newline at end of file + ) + + +@method_decorator(csrf_exempt, name='dispatch') +class FeaturedEventsAPI(APIView): + """Returns events where is_featured=True — used for the homepage hero carousel.""" + + def post(self, request): + try: + user, token, data, error_response = validate_token_and_get_user(request) + if error_response: + return error_response + + events = Event.objects.filter(is_featured=True).order_by('-created_date') + event_list = [] + for e in events: + data_dict = model_to_dict(e) + try: + thumb = EventImages.objects.get(event=e.id, is_primary=True) + data_dict['thumb_img'] = request.build_absolute_uri(thumb.event_image.url) + except EventImages.DoesNotExist: + data_dict['thumb_img'] = '' + event_list.append(data_dict) + + return JsonResponse({status: success, events: event_list}) + except Exception as e: + return JsonResponse({status: error, message: str(e)}) + + +@method_decorator(csrf_exempt, name='dispatch') +class TopEventsAPI(APIView): + """Returns events where is_top_event=True — used for the Top Events section.""" + + def post(self, request): + try: + user, token, data, error_response = validate_token_and_get_user(request) + if error_response: + return error_response + + events = Event.objects.filter(is_top_event=True).order_by('-created_date') + event_list = [] + for e in events: + data_dict = model_to_dict(e) + try: + thumb = EventImages.objects.get(event=e.id, is_primary=True) + data_dict['thumb_img'] = request.build_absolute_uri(thumb.event_image.url) + except EventImages.DoesNotExist: + data_dict['thumb_img'] = '' + event_list.append(data_dict) + + return JsonResponse({status: success, events: event_list}) + except Exception as e: + return JsonResponse({status: error, message: str(e)})