small updates
This commit is contained in:
1 parent
25632e1938
commit
965af7e4ee
5 files changed
+57
-16
No files matched your search
+18
-6
@@ -1,6 +1,8 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
from functools import cache
|
||||
import pathlib
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from monitor_utils.constants import PROXIES_PATH, PROJECT_ROOT
|
||||
@@ -22,6 +24,13 @@ class Settings(BaseSettings):
|
||||
|
||||
model_config = SettingsConfigDict(env_file=(".env", ".env.local"), extra="ignore")
|
||||
|
||||
@property
|
||||
def bot_token(self) -> str:
|
||||
if self.development_mode == DevelopmentMode.PRODUCTION:
|
||||
return self.production_bot_token
|
||||
|
||||
return self.development_bot_token
|
||||
|
||||
|
||||
@cache
|
||||
def get_settings() -> Settings:
|
||||
@@ -39,11 +48,14 @@ def get_proxies() -> list[str]:
|
||||
return proxies
|
||||
|
||||
|
||||
def get_config(config_name: str, use_production_path: bool = False) -> dict:
|
||||
config_name = config_name.replace(".json", "")
|
||||
def get_config(
|
||||
monitor_name: str, use_production_path: bool = False
|
||||
) -> tuple[dict, pathlib.Path]:
|
||||
monitor_name = monitor_name.replace(".json", "")
|
||||
if use_production_path:
|
||||
config_path = PROJECT_ROOT / "configs" / f"{config_name}.json"
|
||||
config_path = PROJECT_ROOT / "configs" / f"{monitor_name}.json"
|
||||
else:
|
||||
config_path = PROJECT_ROOT / "tests" / "configs" / f"{config_name}.json"
|
||||
with open(config_path) as config:
|
||||
return json.load(config)
|
||||
config_path = PROJECT_ROOT / "tests" / "configs" / f"{monitor_name}.json"
|
||||
|
||||
with open(config_path) as f:
|
||||
return json.load(f), config_path
|
||||
@@ -11,6 +11,10 @@ PROXIES_PATH = pathlib.Path("proxies.txt")
|
||||
|
||||
RETRY_LIMIT = 5
|
||||
RETRY_WAIT = 3
|
||||
BACKOFF_DELAY = 0.001
|
||||
FAILURE_DELAY = 0.001
|
||||
MAX_BACKOFF = 10
|
||||
|
||||
TIMEOUT_SECONDS = 30
|
||||
CLIENT_IDENTIFIERS = ["chrome", "chrome_android", "safari", "safari_ios"]
|
||||
CLIENT_TIMEOUT = aiohttp.ClientTimeout(
|
||||
@@ -32,6 +36,10 @@ BUCKET_WEBHOOK_MAP = {
|
||||
"price_drop": "40-49",
|
||||
}
|
||||
MONITOR_NAMES = ["bestbuy", "woot", "lg", "hp", "samsung"]
|
||||
STATUS_PENDING = "pending"
|
||||
STATUS_SENT = "sent"
|
||||
STATUS_SKIPPED = "skipped"
|
||||
STATUS_FAILED = "failed"
|
||||
|
||||
EXPECTED_TASK_ERRORS = (
|
||||
RequestsError,
|
||||
|
||||
@@ -55,20 +55,20 @@ async def add_products_to_database(
|
||||
for product in products:
|
||||
document = convert_product_to_mongo(product, category_identifier)
|
||||
doc_id = document["_id"]
|
||||
|
||||
old_price = existing_prices.get(doc_id)
|
||||
new_price = document.get("currentPrice")
|
||||
|
||||
price_dropped = old_price is None or (
|
||||
new_price is not None and new_price < old_price
|
||||
)
|
||||
|
||||
if price_dropped:
|
||||
document["webhookSent"] = False
|
||||
document["status"] = STATUS_PENDING
|
||||
document["priceBeforeDrop"] = old_price
|
||||
|
||||
set_on_insert = {"discoveredDate": datetime.now(timezone.utc)}
|
||||
if not price_dropped:
|
||||
set_on_insert["webhookSent"] = False
|
||||
set_on_insert["status"] = STATUS_PENDING
|
||||
|
||||
ops.append(
|
||||
UpdateOne(
|
||||
@@ -81,7 +81,7 @@ async def add_products_to_database(
|
||||
try:
|
||||
await collection.bulk_write(ops, ordered=False)
|
||||
except PyMongoError:
|
||||
LOGGER.exception(f"{category_identifier} - Failed to add products to database!")
|
||||
LOGGER.exception(f"{category_identifier} - failed to add products to database")
|
||||
raise
|
||||
|
||||
|
||||
@@ -91,7 +91,6 @@ def convert_product_to_mongo(product: Product, category_identifier: str) -> dict
|
||||
timing = data["timing"]
|
||||
pricing = data["pricing"]
|
||||
availability = data["availability"]
|
||||
|
||||
current_price = pricing["current_price"]
|
||||
original_price = pricing["original_price"]
|
||||
if isinstance(current_price, str):
|
||||
|
||||
+23
-1
@@ -3,6 +3,28 @@ from dataclasses import field
|
||||
from pydantic.dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConsumerConfig:
|
||||
# used claude to refactor the consumer building in the discord bot
|
||||
"""Configuration for a single retailer's consumer.
|
||||
|
||||
Attributes:
|
||||
monitor_name: Retailer name (matches setup_config key).
|
||||
collection: MongoDB collection for this retailer's products.
|
||||
channel_ids: Discord webhook/channel routing config.
|
||||
min_percentage: Minimum discount % to trigger a ping.
|
||||
ping_threshold: Price floor for the secondary 40% rule (e.g. Woot pings
|
||||
on 40%+ discounts if the item is >= this price). None disables
|
||||
the secondary rule.
|
||||
"""
|
||||
|
||||
monitor_name: str
|
||||
collection: object
|
||||
channel_ids: dict
|
||||
min_percentage: int
|
||||
ping_threshold: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PricingInfo:
|
||||
original_price: float
|
||||
@@ -43,7 +65,7 @@ class Product:
|
||||
product_id: str
|
||||
title: str
|
||||
url: str
|
||||
pricing: PricingInfo
|
||||
metadata: ProductMetadata = field(default_factory=ProductMetadata)
|
||||
timing: ProductTiming = field(default_factory=ProductTiming)
|
||||
pricing: PricingInfo = field(default_factory=PricingInfo)
|
||||
availability: AvailabilityInfo = field(default_factory=AvailabilityInfo)
|
||||
@@ -33,16 +33,16 @@ def configure_logging():
|
||||
)
|
||||
|
||||
|
||||
def setup_config(config_name: str) -> tuple[list[str], dict, AsyncIOMotorCollection]:
|
||||
def setup_config(monitor_name: str) -> tuple[list[str], dict, AsyncIOMotorCollection]:
|
||||
is_production = get_settings().development_mode == "PRODUCTION"
|
||||
config = get_config(
|
||||
config_name=config_name,
|
||||
config, _ = get_config(
|
||||
monitor_name=monitor_name,
|
||||
use_production_path=is_production,
|
||||
)
|
||||
|
||||
category_names = config["categories"]
|
||||
collection = get_collection(
|
||||
config_name if is_production else f"{config_name}_testing"
|
||||
monitor_name if is_production else f"{monitor_name}_testing"
|
||||
)
|
||||
return category_names, config, collection
|
||||
|
||||
|
||||
Reference in new issue
Block a user