Files
orion/app/modules/prospecting/routes/api/admin_leads.py
Samir Boulahtit 6d6eba75bf
Some checks failed
CI / pytest (push) Failing after 48m31s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
CI / ruff (push) Successful in 11s
CI / validate (push) Successful in 23s
CI / dependency-scanning (push) Successful in 28s
feat(prospecting): add complete prospecting module for lead discovery and scoring
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>
2026-02-28 00:59:47 +01:00

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"},
)