Some checks failed
Migrates scanning pipeline from marketing-.lu-domains app into Orion module. Supports digital (domain scan) and offline (manual capture) lead channels with enrichment, scoring, campaign management, and interaction tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
# app/modules/prospecting/routes/api/admin_leads.py
|
|
"""
|
|
Admin API routes for lead filtering and export.
|
|
"""
|
|
|
|
import logging
|
|
from math import ceil
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from fastapi.responses import StreamingResponse
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import get_current_admin_api
|
|
from app.core.database import get_db
|
|
from app.modules.prospecting.services.lead_service import lead_service
|
|
from app.modules.tenancy.schemas.auth import UserContext
|
|
|
|
router = APIRouter(prefix="/leads")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@router.get("")
|
|
def list_leads(
|
|
page: int = Query(1, ge=1),
|
|
per_page: int = Query(20, ge=1, le=100),
|
|
min_score: int = Query(0, ge=0, le=100),
|
|
max_score: int = Query(100, ge=0, le=100),
|
|
lead_tier: str | None = Query(None),
|
|
channel: str | None = Query(None),
|
|
has_email: bool | None = Query(None),
|
|
has_phone: bool | None = Query(None),
|
|
reason_flag: str | None = Query(None),
|
|
db: Session = Depends(get_db),
|
|
current_admin: UserContext = Depends(get_current_admin_api),
|
|
):
|
|
"""Get filtered leads with scores."""
|
|
leads, total = lead_service.get_leads(
|
|
db,
|
|
page=page,
|
|
per_page=per_page,
|
|
min_score=min_score,
|
|
max_score=max_score,
|
|
lead_tier=lead_tier,
|
|
channel=channel,
|
|
has_email=has_email,
|
|
has_phone=has_phone,
|
|
reason_flag=reason_flag,
|
|
)
|
|
return {
|
|
"items": leads,
|
|
"total": total,
|
|
"page": page,
|
|
"per_page": per_page,
|
|
"pages": ceil(total / per_page) if per_page else 0,
|
|
}
|
|
|
|
|
|
@router.get("/top-priority")
|
|
def top_priority_leads(
|
|
limit: int = Query(50, ge=1, le=200),
|
|
db: Session = Depends(get_db),
|
|
current_admin: UserContext = Depends(get_current_admin_api),
|
|
):
|
|
"""Get top priority leads (score >= 70)."""
|
|
return lead_service.get_top_priority(db, limit=limit)
|
|
|
|
|
|
@router.get("/quick-wins")
|
|
def quick_win_leads(
|
|
limit: int = Query(50, ge=1, le=200),
|
|
db: Session = Depends(get_db),
|
|
current_admin: UserContext = Depends(get_current_admin_api),
|
|
):
|
|
"""Get quick win leads (score 50-69)."""
|
|
return lead_service.get_quick_wins(db, limit=limit)
|
|
|
|
|
|
@router.get("/export/csv")
|
|
def export_leads_csv(
|
|
min_score: int = Query(0, ge=0, le=100),
|
|
lead_tier: str | None = Query(None),
|
|
channel: str | None = Query(None),
|
|
limit: int = Query(1000, ge=1, le=10000),
|
|
db: Session = Depends(get_db),
|
|
current_admin: UserContext = Depends(get_current_admin_api),
|
|
):
|
|
"""Export filtered leads as CSV download."""
|
|
csv_content = lead_service.export_csv(
|
|
db,
|
|
min_score=min_score,
|
|
lead_tier=lead_tier,
|
|
channel=channel,
|
|
limit=limit,
|
|
)
|
|
return StreamingResponse(
|
|
iter([csv_content]),
|
|
media_type="text/csv",
|
|
headers={"Content-Disposition": "attachment; filename=leads_export.csv"},
|
|
)
|