Files
Priceflow/app/schemas.py
T
Claude 3b095801c5 feat: Replace DuckDuckGo with direct site search
- Remove duckduckgo-search dependency (unreliable results)
- Add direct_search_service.py that scrapes sites' search pages directly
- Add search_url and product_link_selector fields to SearchSite model
- Add beautifulsoup4 and lxml for HTML parsing
- Update schemas with new fields
- Add database migration for new columns

Direct search is more reliable as it queries each site's own search
functionality instead of relying on external search engines.
2025-11-22 15:00:49 +00:00

130 lines
3.3 KiB
Python

from datetime import datetime
from pydantic import BaseModel, ConfigDict
class NotificationProfileCreate(BaseModel):
name: str
apprise_url: str
notify_on_price_drop: bool = True
notify_on_target_price: bool = True
price_drop_threshold_percent: float = 10.0
notify_on_stock_change: bool = True
check_interval_minutes: int = 60
class NotificationProfileUpdate(NotificationProfileCreate):
pass
class NotificationProfileResponse(NotificationProfileCreate):
id: int
model_config = ConfigDict(from_attributes=True)
class ItemCreate(BaseModel):
url: str
name: str
selector: str | None = None
target_price: float | None = None
check_interval_minutes: int = 60
tags: str | None = None
description: str | None = None
notification_profile_id: int | None = None
class ItemResponse(ItemCreate):
id: int
current_price: float | None
in_stock: bool | None
current_price_confidence: float | None = None
in_stock_confidence: float | None = None
is_active: bool
last_checked: datetime | None
is_refreshing: bool = False
last_error: str | None = None
screenshot_url: str | None = None
model_config = ConfigDict(from_attributes=True)
class SettingsUpdate(BaseModel):
key: str
value: str
# === Search Sites Schemas ===
class SearchSiteCreate(BaseModel):
name: str
domain: str
logo_url: str | None = None
category: str | None = None
is_active: bool = True
priority: int = 0
requires_js: bool = False
price_selector: str | None = None
search_url: str | None = None # URL avec {query} placeholder, ex: https://amazon.fr/s?k={query}
product_link_selector: str | None = None # Sélecteur CSS pour les liens produits
class SearchSiteUpdate(BaseModel):
name: str | None = None
domain: str | None = None
logo_url: str | None = None
category: str | None = None
is_active: bool | None = None
priority: int | None = None
requires_js: bool | None = None
price_selector: str | None = None
search_url: str | None = None
product_link_selector: str | None = None
class SearchSiteResponse(BaseModel):
id: int
name: str
domain: str
logo_url: str | None = None
category: str | None = None
is_active: bool
priority: int
requires_js: bool
price_selector: str | None = None
search_url: str | None = None
product_link_selector: str | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
model_config = ConfigDict(from_attributes=True)
# === Search Results Schemas ===
class SearchQuery(BaseModel):
query: str
site_ids: list[int] | None = None # If None, use all active sites
max_results: int = 20
class SearchResultItem(BaseModel):
"""Un produit trouvé lors de la recherche"""
url: str
title: str
price: float | None = None
currency: str = "EUR"
in_stock: bool | None = None
image_url: str | None = None
site_name: str
site_domain: str
confidence: float | None = None
error: str | None = None
class SearchProgress(BaseModel):
"""Progression de la recherche pour SSE"""
status: str # "searching", "scraping", "completed", "error"
total: int
completed: int
current_site: str | None = None
results: list[SearchResultItem] = []
message: str | None = None