Files
Priceflow/app/routers/search_sites.py
T
Claude 826d04331c fix: Improve domain matching and add discover-all endpoint
- Improve domain matching in direct_search_service to handle various formats
- Add /discover-all endpoint to discover search URLs for all sites missing one
- Add constants for magic numbers and fix linting issues
- Better domain cleaning in comparison functions
2025-11-22 15:40:59 +00:00

191 lines
6.2 KiB
Python

"""
API Router pour la gestion des sites de recherche
"""
import logging
import os
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import SessionLocal, get_db
from app.schemas import SearchSiteCreate, SearchSiteResponse, SearchSiteUpdate
from app.services import search_service
from app.services.search_url_discovery import discover_search_url
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/search-sites", tags=["search-sites"])
@router.get("", response_model=list[SearchSiteResponse])
async def get_all_sites(db: Session = Depends(get_db)):
"""Récupère tous les sites de recherche"""
sites = search_service.get_all_sites(db)
return sites
@router.get("/{site_id}", response_model=SearchSiteResponse)
async def get_site(site_id: int, db: Session = Depends(get_db)):
"""Récupère un site par son ID"""
site = search_service.get_site_by_id(db, site_id)
if not site:
raise HTTPException(status_code=404, detail="Site not found")
return site
async def _discover_and_update_site(site_id: int, domain: str, db_url: str):
"""Découvre l'URL de recherche en background et met à jour le site"""
try:
logger.info(f"Discovering search URL for site {site_id} ({domain})")
result = await discover_search_url(domain)
if result.get("discovered"):
# Mettre à jour le site avec l'URL découverte
db = SessionLocal()
try:
update_data = {}
if result.get("search_url"):
update_data["search_url"] = result["search_url"]
if result.get("product_link_selector"):
update_data["product_link_selector"] = result["product_link_selector"]
if update_data:
search_service.update_site(db, site_id, update_data)
logger.info(f"Updated site {site_id} with discovered URL: {result.get('search_url')}")
finally:
db.close()
else:
logger.warning(f"Could not discover search URL for {domain}")
except Exception as e:
logger.error(f"Error in background discovery for {domain}: {e}")
@router.post("", response_model=SearchSiteResponse, status_code=201)
async def create_site(
site: SearchSiteCreate,
background_tasks: BackgroundTasks,
db: Session = Depends(get_db),
):
"""
Crée un nouveau site de recherche.
Si search_url n'est pas fourni, tente de le découvrir automatiquement.
"""
try:
new_site = search_service.create_site(db, site.model_dump())
# Si pas de search_url, lancer la découverte en background
if not site.search_url:
db_url = os.getenv("DATABASE_URL", "")
background_tasks.add_task(
_discover_and_update_site,
new_site.id,
new_site.domain,
db_url,
)
logger.info(f"Started background discovery for site {new_site.id}")
return new_site
except Exception as e:
raise HTTPException(status_code=400, detail=str(e)) from e
@router.put("/{site_id}", response_model=SearchSiteResponse)
async def update_site(
site_id: int,
site: SearchSiteUpdate,
db: Session = Depends(get_db),
):
"""Met à jour un site de recherche"""
updated = search_service.update_site(
db, site_id, site.model_dump(exclude_unset=True)
)
if not updated:
raise HTTPException(status_code=404, detail="Site not found")
return updated
@router.delete("/{site_id}", status_code=204)
async def delete_site(site_id: int, db: Session = Depends(get_db)):
"""Supprime un site de recherche"""
deleted = search_service.delete_site(db, site_id)
if not deleted:
raise HTTPException(status_code=404, detail="Site not found")
@router.post("/{site_id}/discover", response_model=dict)
async def discover_site_search_url(
site_id: int,
db: Session = Depends(get_db),
):
"""
Force la découverte de l'URL de recherche pour un site existant.
Utile si la découverte automatique n'a pas fonctionné.
"""
site = search_service.get_site_by_id(db, site_id)
if not site:
raise HTTPException(status_code=404, detail="Site not found")
result = await discover_search_url(site.domain)
if result.get("discovered"):
# Mettre à jour le site
update_data = {}
if result.get("search_url"):
update_data["search_url"] = result["search_url"]
if result.get("product_link_selector"):
update_data["product_link_selector"] = result["product_link_selector"]
if update_data:
search_service.update_site(db, site_id, update_data)
return {
"success": True,
"message": f"URL de recherche découverte: {result.get('search_url')}",
"search_url": result.get("search_url"),
"product_link_selector": result.get("product_link_selector"),
}
else:
return {
"success": False,
"message": "Impossible de découvrir l'URL de recherche automatiquement",
}
@router.post("/discover-all", response_model=dict)
async def discover_all_missing_search_urls(
background_tasks: BackgroundTasks,
db: Session = Depends(get_db),
):
"""
Lance la découverte d'URL de recherche pour tous les sites qui n'en ont pas.
La découverte se fait en arrière-plan.
"""
sites = search_service.get_all_sites(db)
sites_to_discover = [s for s in sites if not s.search_url]
if not sites_to_discover:
return {
"success": True,
"message": "Tous les sites ont déjà une URL de recherche configurée",
"sites_queued": 0,
}
db_url = os.getenv("DATABASE_URL", "")
for site in sites_to_discover:
background_tasks.add_task(
_discover_and_update_site,
site.id,
site.domain,
db_url,
)
return {
"success": True,
"message": f"Découverte lancée pour {len(sites_to_discover)} sites",
"sites_queued": len(sites_to_discover),
"sites": [{"id": s.id, "domain": s.domain} for s in sites_to_discover],
}