RABAC felépítése megtörtént ill hirdetési portál alapok lerakva
This commit is contained in:
136
backend/app/api/v1/endpoints/marketing.py
Normal file
136
backend/app/api/v1/endpoints/marketing.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/marketing.py
|
||||
"""
|
||||
Marketing & Ad Placement Public API Endpoints.
|
||||
|
||||
Provides:
|
||||
- GET /placements/{zone} — Get active ads for a specific placement zone
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select, and_
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.models.marketing import (
|
||||
Placement,
|
||||
CampaignPlacement,
|
||||
Campaign,
|
||||
Creative,
|
||||
CampaignStatus,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class AdResponse(BaseModel):
|
||||
"""Egy hirdetés válaszmodellje a frontend számára."""
|
||||
|
||||
id: int
|
||||
campaign_id: int
|
||||
campaign_name: str
|
||||
creative_id: int
|
||||
creative_type: str
|
||||
content_url: Optional[str] = None
|
||||
html_snippet: Optional[str] = None
|
||||
alt_text: Optional[str] = None
|
||||
target_url: Optional[str] = None
|
||||
priority: int = 0
|
||||
|
||||
|
||||
class PlacementResponse(BaseModel):
|
||||
"""Egy placement zóna összes aktív hirdetésének válasza."""
|
||||
|
||||
zone: str
|
||||
ads: List[AdResponse]
|
||||
|
||||
|
||||
@router.get("/placements/{zone}", response_model=PlacementResponse)
|
||||
async def get_placement_ads(
|
||||
zone: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Aktív hirdetések lekérése egy adott placement zónához.
|
||||
|
||||
**Logika:**
|
||||
1. Megkeresi a Placement rekordot a `name` alapján (case-insensitive).
|
||||
2. Lekéri az összes aktív kampányhoz tartozó CampaignPlacement kapcsolatot.
|
||||
3. Visszaadja a kapcsolódó Creative-okat prioritás szerint rendezve.
|
||||
4. Ha nincs aktív kampány, üres listával tér vissza (nem dob hibát).
|
||||
"""
|
||||
# 1. Find the placement by name (case-insensitive)
|
||||
placement_stmt = select(Placement).where(
|
||||
Placement.name.ilike(zone),
|
||||
Placement.is_active == True,
|
||||
)
|
||||
result = await db.execute(placement_stmt)
|
||||
placement = result.scalar_one_or_none()
|
||||
|
||||
if not placement:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Placement zone '{zone}' not found",
|
||||
)
|
||||
|
||||
# 2. Find active campaign-placement links with eager-loaded relations
|
||||
campaign_placements_stmt = (
|
||||
select(CampaignPlacement)
|
||||
.options(
|
||||
selectinload(CampaignPlacement.campaign),
|
||||
selectinload(CampaignPlacement.creative),
|
||||
)
|
||||
.where(
|
||||
and_(
|
||||
CampaignPlacement.placement_id == placement.id,
|
||||
CampaignPlacement.is_active == True,
|
||||
)
|
||||
)
|
||||
.order_by(CampaignPlacement.priority.asc())
|
||||
)
|
||||
result = await db.execute(campaign_placements_stmt)
|
||||
campaign_placements = result.scalars().all()
|
||||
|
||||
# 3. Filter: only include CampaignPlacements where the campaign is ACTIVE
|
||||
now_utc = datetime.now(timezone.utc)
|
||||
active_ads: List[AdResponse] = []
|
||||
for cp in campaign_placements:
|
||||
campaign = cp.campaign
|
||||
creative = cp.creative
|
||||
|
||||
if not campaign or not creative:
|
||||
continue
|
||||
|
||||
# Check campaign status
|
||||
if campaign.status != CampaignStatus.ACTIVE.value:
|
||||
continue
|
||||
|
||||
# Check campaign date range
|
||||
if campaign.start_date and campaign.start_date > now_utc:
|
||||
continue
|
||||
if campaign.end_date and campaign.end_date < now_utc:
|
||||
continue
|
||||
|
||||
active_ads.append(
|
||||
AdResponse(
|
||||
id=cp.id,
|
||||
campaign_id=campaign.id,
|
||||
campaign_name=campaign.name,
|
||||
creative_id=creative.id,
|
||||
creative_type=creative.creative_type,
|
||||
content_url=creative.content_url,
|
||||
html_snippet=creative.html_snippet,
|
||||
alt_text=creative.alt_text,
|
||||
target_url=creative.target_url,
|
||||
priority=cp.priority,
|
||||
)
|
||||
)
|
||||
|
||||
return PlacementResponse(
|
||||
zone=zone,
|
||||
ads=active_ads,
|
||||
)
|
||||
Reference in New Issue
Block a user