Compare commits

..
19 Commits
Author SHA1 Message Date
dsql 7d514a5401 Merge pull request 'calculate_priority: resolve non-finite prices to 0 instead of crashing the monitor' (#25) from development into main 2026-10-08 22:48:57 +00:00
TPV bd5df3a4c2 Merge pull request 'small cleanup of comments/added optional impersonate into run_bounded_cycle for monitors like bhphoto that need different clients than what we support/dont need to rotate' (#24) from development into main
Reviewed-on: #24
2026-09-30 01:56:40 +00:00
TPV 1b3f860ff3 Merge pull request 'updated tls profiles' (#23) from development into main
Reviewed-on: #23
2026-09-27 04:11:53 +00:00
TPV 7032f5b9d6 Merge pull request 'added get_response_json function to return json from aioweb' (#22) from development into main
Reviewed-on: #22
2026-09-26 03:00:57 +00:00
TPV 4729a62ae2 Merge pull request 'renamed monitor_name to monitor' (#21) from development into main
Reviewed-on: #21
2026-09-26 00:29:32 +00:00
TPV 2bf2c613a9 Merge pull request 'added asins to build_product_details' (#20) from development into main
Reviewed-on: #20
2026-09-10 13:39:02 +00:00
TPV 0fceafb364 Merge pull request 'added price drops to alerts/fixed race condition where late claimed docs got their tags wiped and sent to the api without tags' (#19) from development into main
Reviewed-on: #19
2026-09-09 16:52:30 +00:00
TPV 69484c5ae0 Merge pull request 'added asins for woot listings' (#18) from development into main
Reviewed-on: #18
2026-09-09 03:15:06 +00:00
TPV c9545ec9d8 Merge pull request 'updated the db name for dsql mongo' (#17) from development into main
Reviewed-on: #17
2026-08-31 00:59:22 +00:00
TPV 7f30a8a5cc Merge pull request 'updated the mongo_url to dsqls new url/small type hint fixes' (#16) from development into main
Reviewed-on: #16
2026-08-31 00:55:21 +00:00
TPV 87d751cd82 Merge pull request 'added optional mongo client for already configured monitors' (#15) from development into main
Reviewed-on: #15
2026-08-21 15:22:35 +00:00
TPV 2a5d649f90 Merge pull request 'added optional mongo url' (#14) from development into main
Reviewed-on: #14
2026-08-21 14:52:50 +00:00
TPV 04ce7b50f3 Merge pull request 'bug fix to avoid swallowing exceptions/failed pages inside fetch_all_pages/small refactor/added new listings' (#13) from development into main
Reviewed-on: #13
2026-08-13 20:23:00 +00:00
TPV cc46e3d409 Merge pull request 'removed retry_failed_pages from docstrings' (#12) from development into main
Reviewed-on: #12
2026-08-09 02:00:14 +00:00
TPV 5db82e3a59 Merge pull request 'removed failed page retrying' (#11) from development into main
Reviewed-on: #11
2026-08-08 20:51:09 +00:00
TPV 95af8a37e1 Merge pull request 'added no msrp prirority so they don't starve in queue' (#10) from development into main
Reviewed-on: #10
2026-08-04 20:54:19 +00:00
TPV 2ede6c77c7 Merge pull request 'use .get for has_msrp' (#9) from development into main
Reviewed-on: #9
2026-08-04 18:49:45 +00:00
TPV b34db200cc Merge pull request 'small typo fix' (#8) from development into main
Reviewed-on: #8
2026-08-04 17:07:56 +00:00
TPV 7c420059ee Merge pull request 'added has_msrp for no msrp based products(new lists)' (#7) from development into main
Reviewed-on: #7
2026-08-04 17:05:36 +00:00
2 changed files with 3 additions and 66 deletions

No files matched your search

+3 -28
View File
@@ -1,6 +1,5 @@
import asyncio
import logging
import math
from collections.abc import Awaitable, Callable, Iterable
from contextlib import asynccontextmanager, nullcontext
@@ -161,10 +160,7 @@ def build_product_details(
) -> Product | None:
"""Build a Product from a Mongo document if it passes discount thresholds.
Skips documents with a missing or zero original price, a non-finite
price, or a discount too large to represent: a retailer field like
"Infinity" survives float(), and the percentage math used to raise
OverflowError out of every caller. Passing requires
Skips documents with a missing or zero original price. Passing requires
meeting min_percentage, or min_price_drop when provided. The caller may
bypass both thresholds (new listings send at any discount for monitors
that opt in); which alerts qualify is the caller's decision, not read
@@ -177,7 +173,7 @@ def build_product_details(
min_percentage: Minimum discount percentage to pass.
min_price_drop: Optional minimum absolute price drop to pass.
bypass_thresholds: Build the product even when it fails both
thresholds. The missing- and non-finite-price skips still apply.
thresholds. The missing-price skip still applies.
Returns:
The built Product, or None when skipped or below thresholds.
@@ -195,19 +191,6 @@ def build_product_details(
)
return
# Skipped on BOTH paths, not just the percentage one: an MSRP-less product
# would otherwise carry the infinite price into its payload. Only floats can
# be non-finite, so ints and other types keep their existing behaviour.
if any(
isinstance(price, float) and not math.isfinite(price)
for price in (current_price, original_price)
):
LOGGER.warning(
"non-finite price, skipping",
extra={"product_id": product_id},
)
return
percentage = 0
# Defaulted, not indexed: documents written before the field existed have no
# hasMsrp at all, and stale ones that drop out of a monitor's feed are never
@@ -215,15 +198,7 @@ def build_product_details(
# pre-existing threshold path rather than silently routing them as MSRP-less.
has_msrp = product.get("hasMsrp", True)
if has_msrp:
discount = ((original_price - current_price) / original_price) * 100
if not math.isfinite(discount):
LOGGER.warning(
"discount not representable, skipping",
extra={"product_id": product_id},
)
return
percentage = int(discount)
percentage = int(((original_price - current_price) / original_price) * 100)
price_drop = original_price - current_price
# A percentage code compounds onto the sale price, so an item can clear the
# threshold on the combined discount even when the base discount would not.
-38
View File
@@ -203,41 +203,3 @@ def test_msrpless_doc_with_prices_builds_unthresholded():
product = build_product_details(doc, min_percentage=40)
assert product is not None
assert product.pricing.has_msrp is False
# ------------------------------------------------------------ non-finite prices
def test_infinite_price_skips_instead_of_raising():
"""Regression (2026-10-09 06:25Z): float("Infinity") from a retailer field
raised OverflowError here and stopped the consumer's API deal loop."""
inf = float("inf")
assert build_product_details(_doc(originalPrice=inf), min_percentage=40) is None
assert build_product_details(_doc(currentPrice=inf), min_percentage=40) is None
assert build_product_details(_doc(currentPrice=-inf), min_percentage=40) is None
def test_nan_price_skips_instead_of_raising():
nan = float("nan")
assert build_product_details(_doc(currentPrice=nan), min_percentage=40) is None
assert build_product_details(_doc(originalPrice=nan), min_percentage=40) is None
def test_msrpless_infinite_price_skips_rather_than_reaching_the_payload():
"""Without the skip the MSRP-less path would carry inf into the Product."""
doc = _doc(hasMsrp=False, currentPrice=float("inf"))
assert build_product_details(doc, min_percentage=40) is None
def test_bypass_does_not_rescue_a_non_finite_price():
doc = _doc(originalPrice=float("inf"))
assert build_product_details(doc, min_percentage=40, bypass_thresholds=True) is None
def test_unrepresentable_discount_skips_instead_of_raising():
"""Finite prices can still overflow the ratio (a subnormal original)."""
doc = _doc(currentPrice=-1e308, originalPrice=5e-324)
assert build_product_details(doc, min_percentage=40) is None