small updates

This commit is contained in:
tpv committed 2026-06-19 10:18:23 -07:00
1 parent 25632e1938
commit 965af7e4ee
5 files changed
+57 -16

No files matched your search

+18 -6
View File
@@ -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
+8
View File
@@ -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,
+4 -5
View File
@@ -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
View File
@@ -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)
+4 -4
View File
@@ -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