Files
eventify_backend/ad_control/management/commands/seed_surfaces.py
Sicherhaven b2a2cbad5f feat(ad_control): new AdSurface + AdPlacement module for placement-based featured/top events
- New ad_control Django app: AdSurface + AdPlacement models with GLOBAL/LOCAL scope
- Admin CRUD API at /api/v1/ad-control/ (JWT-protected): surfaces, placements, picker events
- Placement lifecycle: DRAFT → ACTIVE|SCHEDULED → EXPIRED|DISABLED
- LOCAL scope: Haversine ≤ 50km from event lat/lng (fixed radius, no config needed)
- Consumer APIs: /api/events/featured-events/ and /api/events/top-events/ rewritten
  to use placement-based queries (same URL paths + response shape — no breaking changes)
- Seed command: seed_surfaces --migrate converts existing is_featured/is_top_event booleans
- mount: admin_api/urls.py → ad-control/, mobile_api/urls.py → replaced consumer views
- settings.py: added ad_control to INSTALLED_APPS
2026-04-06 12:10:06 +05:30

101 lines
3.4 KiB
Python

"""
Seed the default AdSurface records and migrate existing boolean flags to placements.
Usage:
python manage.py seed_surfaces # seed surfaces only
python manage.py seed_surfaces --migrate # also migrate is_featured / is_top_event to placements
"""
from django.core.management.base import BaseCommand
from ad_control.models import AdSurface, AdPlacement
from events.models import Event
SURFACES = [
{
'key': 'HOME_FEATURED_CAROUSEL',
'name': 'Featured Carousel',
'description': 'Homepage hero carousel — high-impact banner-style placement.',
'max_slots': 8,
'layout_type': 'carousel',
'sort_behavior': 'rank',
},
{
'key': 'HOME_TOP_EVENTS',
'name': 'Top Events',
'description': 'Homepage "Top Events" grid section below the hero.',
'max_slots': 10,
'layout_type': 'grid',
'sort_behavior': 'rank',
},
]
class Command(BaseCommand):
help = 'Seed default ad surfaces and optionally migrate boolean flags to placements.'
def add_arguments(self, parser):
parser.add_argument(
'--migrate',
action='store_true',
help='Also migrate existing is_featured / is_top_event flags to AdPlacement rows.',
)
def handle(self, *args, **options):
# --- Seed surfaces ---
for s in SURFACES:
obj, created = AdSurface.objects.update_or_create(
key=s['key'],
defaults=s,
)
status = 'CREATED' if created else 'EXISTS'
self.stdout.write(f" [{status}] {obj.key}{obj.name}")
# --- Migrate boolean flags ---
if options['migrate']:
self.stdout.write('\nMigrating boolean flags to placements...')
featured_surface = AdSurface.objects.get(key='HOME_FEATURED_CAROUSEL')
top_surface = AdSurface.objects.get(key='HOME_TOP_EVENTS')
featured_events = Event.objects.filter(is_featured=True)
top_events = Event.objects.filter(is_top_event=True)
created_count = 0
for rank, event in enumerate(featured_events, start=1):
_, created = AdPlacement.objects.get_or_create(
surface=featured_surface,
event=event,
defaults={
'status': 'ACTIVE',
'priority': 'MANUAL',
'scope': 'GLOBAL',
'rank': rank,
'boost_label': 'Featured',
},
)
if created:
created_count += 1
for rank, event in enumerate(top_events, start=1):
_, created = AdPlacement.objects.get_or_create(
surface=top_surface,
event=event,
defaults={
'status': 'ACTIVE',
'priority': 'MANUAL',
'scope': 'GLOBAL',
'rank': rank,
'boost_label': 'Top Event',
},
)
if created:
created_count += 1
self.stdout.write(self.style.SUCCESS(
f' Migrated {created_count} placements '
f'({featured_events.count()} featured, {top_events.count()} top events).'
))
self.stdout.write(self.style.SUCCESS('\nDone.'))