Files
Priceflow/app/models.py
T

99 lines
4.9 KiB
Python

from datetime import UTC, datetime
from sqlalchemy import Boolean, Column, DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship
from .database import Base
class User(Base):
"""User model for authentication"""
__tablename__ = "users"
id: int = Column(Integer, primary_key=True, index=True) # type: ignore
username: str = Column(String, unique=True, nullable=False, index=True) # type: ignore
password_hash: str = Column(String, nullable=False) # type: ignore
is_admin: bool = Column(Boolean, default=False) # type: ignore
is_active: bool = Column(Boolean, default=True) # type: ignore
created_at: datetime = Column(DateTime, default=lambda: datetime.now(UTC)) # type: ignore
last_login: datetime | None = Column(DateTime, nullable=True) # type: ignore
class Item(Base):
__tablename__ = "items"
id: int = Column(Integer, primary_key=True, index=True) # type: ignore
url: str = Column(String, index=True) # type: ignore
name: str = Column(String) # type: ignore
selector: str | None = Column(String, nullable=True) # type: ignore
target_price: float | None = Column(Float, nullable=True) # type: ignore
check_interval_minutes: int = Column(Integer, default=60) # type: ignore
# New fields
current_price: float | None = Column(Float, nullable=True) # type: ignore
in_stock: bool | None = Column(Boolean, nullable=True) # type: ignore
is_available: bool | None = Column(Boolean, nullable=True, default=True) # type: ignore # Product still exists
category: str | None = Column(String, nullable=True) # type: ignore # User-defined category
tags: str | None = Column(String, nullable=True) # type: ignore
description: str | None = Column(String, nullable=True) # type: ignore
# Confidence scores for latest extraction
current_price_confidence: float | None = Column(Float, nullable=True) # type: ignore
in_stock_confidence: float | None = Column(Float, nullable=True) # type: ignore
is_active: bool = Column(Boolean, default=True) # type: ignore
last_checked: datetime | None = Column(DateTime, nullable=True) # type: ignore
is_refreshing: bool = Column(Boolean, default=False) # type: ignore
last_error: str | None = Column(String, nullable=True) # type: ignore
price_history = relationship("PriceHistory", back_populates="item", cascade="all, delete-orphan")
class PriceHistory(Base):
__tablename__ = "price_history"
id: int = Column(Integer, primary_key=True, index=True) # type: ignore
item_id: int = Column(Integer, ForeignKey("items.id")) # type: ignore
price: float = Column(Float) # type: ignore
timestamp: datetime = Column(DateTime, default=lambda: datetime.now(UTC)) # type: ignore
screenshot_path: str | None = Column(String, nullable=True) # type: ignore
# Confidence scores and AI metadata
price_confidence: float | None = Column(Float, nullable=True) # type: ignore
in_stock_confidence: float | None = Column(Float, nullable=True) # type: ignore
ai_model: str | None = Column(String, nullable=True) # type: ignore
ai_provider: str | None = Column(String, nullable=True) # type: ignore
prompt_version: str | None = Column(String, nullable=True) # type: ignore
repair_used: bool | None = Column(Boolean, nullable=True, default=False) # type: ignore
item = relationship("Item", back_populates="price_history")
class Settings(Base):
__tablename__ = "settings"
key = Column(String, primary_key=True, index=True)
value = Column(Text)
class SearchSite(Base):
"""Sites configurés pour la recherche de produits"""
__tablename__ = "search_sites"
id: int = Column(Integer, primary_key=True, index=True) # type: ignore
name: str = Column(String, nullable=False) # type: ignore # "Amazon France"
domain: str = Column(String, unique=True, nullable=False, index=True) # type: ignore # "amazon.fr"
logo_url: str | None = Column(String, nullable=True) # type: ignore
category: str | None = Column(String, nullable=True) # type: ignore # "Tech", "Déco", "Généraliste"
is_active: bool = Column(Boolean, default=True) # type: ignore
priority: int = Column(Integer, default=0) # type: ignore # Ordre d'affichage
requires_js: bool = Column(Boolean, default=False) # type: ignore # Force Browserless si True
price_selector: str | None = Column(String, nullable=True) # type: ignore # Sélecteur CSS pour le prix
search_url: str | None = Column(String, nullable=True) # type: ignore # URL de recherche avec {query} placeholder
product_link_selector: str | None = Column(String, nullable=True) # type: ignore # Sélecteur CSS pour les liens produits
created_at: datetime = Column(DateTime, default=lambda: datetime.now(UTC)) # type: ignore
updated_at: datetime = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC)) # type: ignore