sushilideaclan01's picture
refactored the files
d4a4da7
"""Database endpoints: stats, list ads, get/delete ad, edit copy."""
from typing import Optional
from fastapi import APIRouter, HTTPException, Depends
from api.schemas import DbStatsResponse, EditAdCopyRequest
from services.database import db_service
from services.auth_dependency import get_current_user
router = APIRouter(tags=["database"])
@router.get("/db/stats", response_model=DbStatsResponse)
async def get_database_stats(username: str = Depends(get_current_user)):
"""Get statistics about stored ad creatives for the current user."""
return await db_service.get_stats(username=username)
@router.get("/db/ads")
async def list_stored_ads(
niche: Optional[str] = None,
generation_method: Optional[str] = None,
limit: int = 50,
offset: int = 0,
username: str = Depends(get_current_user),
):
"""List ad creatives for the current user with optional filters and pagination."""
ads, total = await db_service.list_ad_creatives(
username=username,
niche=niche,
generation_method=generation_method,
limit=limit,
offset=offset,
)
return {
"total": total,
"limit": limit,
"offset": offset,
"ads": [
{
"id": str(ad.get("id", "")),
"niche": ad.get("niche", ""),
"title": ad.get("title"),
"headline": ad.get("headline", ""),
"primary_text": ad.get("primary_text"),
"description": ad.get("description"),
"body_story": ad.get("body_story"),
"cta": ad.get("cta", ""),
"psychological_angle": ad.get("psychological_angle", ""),
"image_url": ad.get("image_url"),
"r2_url": ad.get("r2_url"),
"image_filename": ad.get("image_filename"),
"image_model": ad.get("image_model"),
"angle_key": ad.get("angle_key"),
"concept_key": ad.get("concept_key"),
"generation_method": ad.get("generation_method", "standard"),
"created_at": ad.get("created_at"),
}
for ad in ads
],
}
@router.get("/db/ad/{ad_id}")
async def get_stored_ad(ad_id: str):
"""Get a specific ad creative by ID."""
ad = await db_service.get_ad_creative(ad_id)
if not ad:
raise HTTPException(status_code=404, detail=f"Ad '{ad_id}' not found")
return {
"id": str(ad.get("id", "")),
"niche": ad.get("niche", ""),
"title": ad.get("title"),
"headline": ad.get("headline", ""),
"primary_text": ad.get("primary_text"),
"description": ad.get("description"),
"body_story": ad.get("body_story"),
"cta": ad.get("cta", ""),
"psychological_angle": ad.get("psychological_angle", ""),
"why_it_works": ad.get("why_it_works"),
"image_url": ad.get("image_url"),
"image_filename": ad.get("image_filename"),
"image_model": ad.get("image_model"),
"image_seed": ad.get("image_seed"),
"r2_url": ad.get("r2_url"),
"angle_key": ad.get("angle_key"),
"angle_name": ad.get("angle_name"),
"angle_trigger": ad.get("angle_trigger"),
"angle_category": ad.get("angle_category"),
"concept_key": ad.get("concept_key"),
"concept_name": ad.get("concept_name"),
"concept_structure": ad.get("concept_structure"),
"concept_visual": ad.get("concept_visual"),
"concept_category": ad.get("concept_category"),
"generation_method": ad.get("generation_method", "standard"),
"metadata": ad.get("metadata"),
"created_at": ad.get("created_at"),
"updated_at": ad.get("updated_at"),
}
@router.delete("/db/ad/{ad_id}")
async def delete_stored_ad(ad_id: str, username: str = Depends(get_current_user)):
"""Delete an ad creative. Users can only delete their own ads."""
success = await db_service.delete_ad_creative(ad_id, username=username)
if not success:
raise HTTPException(status_code=404, detail=f"Ad '{ad_id}' not found or could not be deleted")
return {"success": True, "deleted_id": ad_id}
@router.post("/db/ad/edit")
async def edit_ad_copy(
request: EditAdCopyRequest,
username: str = Depends(get_current_user),
):
"""
Edit ad copy fields. Modes: manual (direct update) or ai (AI-improved version).
"""
from services.llm import LLMService
ad = await db_service.get_ad_creative(request.ad_id)
if not ad:
raise HTTPException(status_code=404, detail=f"Ad '{request.ad_id}' not found")
if ad.get("username") != username:
raise HTTPException(status_code=403, detail="You can only edit your own ads")
if request.mode == "manual":
success = await db_service.update_ad_creative(
ad_id=request.ad_id,
username=username,
**{request.field: request.value},
)
if not success:
raise HTTPException(status_code=500, detail="Failed to update ad")
return {"edited_value": request.value, "success": True}
llm_service = LLMService()
field_labels = {
"title": "title",
"headline": "headline",
"primary_text": "primary text",
"description": "description",
"body_story": "body story",
"cta": "call to action",
}
field_label = field_labels.get(request.field, request.field)
current_value = request.value
niche = ad.get("niche", "general")
system_prompt = f"""You are an expert copywriter specializing in high-converting ad copy for {niche.replace('_', ' ')}.
Your task is to improve the {field_label} while maintaining its core message and emotional impact.
Keep the same tone and style, but make it more compelling, clear, and effective."""
user_prompt = f"""Current {field_label}:\n{current_value}\n\n"""
if request.user_suggestion:
user_prompt += f"User's suggestion: {request.user_suggestion}\n\n"
user_prompt += f"""Please provide an improved version of this {field_label} that:
1. Maintains the core message and emotional impact
2. Is more compelling and engaging
3. Follows best practices for {field_label} in ad copy
4. {"Incorporates the user's suggestion" if request.user_suggestion else "Is optimized for conversion"}
Return ONLY the improved {field_label} text, without any explanations or additional text."""
try:
edited_value = await llm_service.generate(
prompt=user_prompt,
system_prompt=system_prompt,
temperature=0.7,
)
edited_value = edited_value.strip().strip('"').strip("'")
return {"edited_value": edited_value, "success": True}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to generate AI edit: {str(e)}")