From b876c3d7b31241ed52e73c1be3e9f50f20a5fe61 Mon Sep 17 00:00:00 2001 From: TPV Date: Sun, 26 Jul 2026 14:09:05 -0700 Subject: [PATCH] removed timeout from send_request and added promo options to product metadata --- src/monitor_utils/models.py | 14 ++++++ src/monitor_utils/sessions.py | 1 - tests/test_convert_product_to_mongo.py | 59 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/monitor_utils/models.py b/src/monitor_utils/models.py index 34cfe4d..ebd5c13 100644 --- a/src/monitor_utils/models.py +++ b/src/monitor_utils/models.py @@ -97,6 +97,16 @@ class ProductMetadata: overstock_text: Overstock label text when present. percentage: Computed discount percentage. affiliate_url: Affiliate link when available. + promo_code: Event-level checkout code (e.g. "BIRTHDAY26") when the + product's event advertises one, else None. + promo_percentage: Advertised percent off from a percentage code + ("extra 22% off" -> 22); None for dollar codes or no code. This is + the claimed discount, not the actual price delta. + promo_dollar_off: Advertised dollar amount off from a spend-threshold + code ("get an extra $22 off" -> 22); None for percentage codes or + no code. Pairs with promo_min_spend. + promo_min_spend: Spend threshold a dollar code requires ("Spend $100" + -> 100); None unless promo_dollar_off is set. """ image_url: str | None = None @@ -106,6 +116,10 @@ class ProductMetadata: overstock_text: str | None = None percentage: int | None = None affiliate_url: str | None = None + promo_code: str | None = None + promo_percentage: int | None = None + promo_dollar_off: int | None = None + promo_min_spend: int | None = None @dataclass diff --git a/src/monitor_utils/sessions.py b/src/monitor_utils/sessions.py index e422507..c08b0b1 100644 --- a/src/monitor_utils/sessions.py +++ b/src/monitor_utils/sessions.py @@ -11,7 +11,6 @@ import random from aioproxies import AioProxies from aioweb import Response from aioweb_tls import CurlCffi, TLSSession - from monitor_utils.constants import CLIENT_IDENTIFIERS, RETRY_LIMIT, TIMEOUT_SECONDS diff --git a/tests/test_convert_product_to_mongo.py b/tests/test_convert_product_to_mongo.py index a68147e..f289317 100644 --- a/tests/test_convert_product_to_mongo.py +++ b/tests/test_convert_product_to_mongo.py @@ -11,15 +11,16 @@ Product), which is why convert_product_to_mongo no longer coerces them itself. from datetime import datetime from monitor_utils.database import convert_product_to_mongo -from monitor_utils.models import PricingInfo, Product +from monitor_utils.models import PricingInfo, Product, ProductMetadata -def _product(**pricing) -> Product: +def _product(metadata: ProductMetadata | None = None, **pricing) -> Product: return Product( product_id="p1", title="Widget", url="http://x/p1", pricing=PricingInfo(**pricing), + metadata=metadata or ProductMetadata(), ) @@ -46,3 +47,57 @@ def test_core_fields_map_through(): doc = convert_product_to_mongo(_product(original_price=200.0, current_price=150.0)) assert doc["title"] == "Widget" assert doc["url"] == "http://x/p1" + + +def test_promo_and_affiliate_fields_serialize(): + """promo specifics + affiliate url reach the document under camelCase keys""" + + doc = convert_product_to_mongo( + _product( + metadata=ProductMetadata( + affiliate_url="http://aff/p1", + promo_code="BIRTHDAY26", + promo_percentage=22, + promo_dollar_off=None, + promo_min_spend=None, + ), + original_price=200.0, + current_price=150.0, + ) + ) + assert doc["affiliateUrl"] == "http://aff/p1" + assert doc["promoCode"] == "BIRTHDAY26" + assert doc["promoPercentage"] == 22 + assert doc["promoDollarOff"] is None + assert doc["promoMinSpend"] is None + + +def test_dollar_promo_fields_serialize(): + """a spend-threshold code stores amount + min spend, not a percentage""" + + doc = convert_product_to_mongo( + _product( + metadata=ProductMetadata( + promo_code="STORE22", + promo_dollar_off=22, + promo_min_spend=100, + ), + original_price=200.0, + current_price=150.0, + ) + ) + assert doc["promoCode"] == "STORE22" + assert doc["promoDollarOff"] == 22 + assert doc["promoMinSpend"] == 100 + assert doc["promoPercentage"] is None + + +def test_promo_fields_default_to_none(): + """a product with no promo still carries the keys as None (uniform doc shape)""" + + doc = convert_product_to_mongo(_product(original_price=200.0, current_price=150.0)) + for key in ("promoCode", "promoPercentage", "promoDollarOff", "promoMinSpend"): + assert doc[key] is None + # percentage and channelId stay OUT of the doc on purpose + assert "percentage" not in doc + assert "channelId" not in doc