Skip to content

Restock & Price detection - decimal-comma prices ("39,99") are read as 3999, the comma is stripped before parse_currency() #4493

Closed

Description

@jaime-king

Describe the bug
When a page's structured data gives the price with a decimal comma, the Restock & Price processor stores the price 100× too high. Decimal commas are normal for EUR shops, e.g. itemprop="price" content="39,99" or JSON-LD "price": "39,99", and 39,99 is stored as 3999.0 instead of 39.99.

Root cause
Restock.parse_currency() (processors/restock_diff/__init__.py:19) already handles decimal commas: parse_currency("39,99") → 39.99. But the extractors strip everything except [\d.] and call float() before it ever sees the value, so the comma is gone:

# processors/restock_diff/processor.py:54 (_deduplicate_prices), same at :50
v = float(re.sub(r'[^\d.]', '', str(datum.value)))     # "39,99" -> "3999" -> 3999.0

The same pattern is at processors/restock_diff/pure_python_extractor.py:244 (JSON-LD string fallback) and :138 (microdata text node).

Change detection still works because both prices are off by the same factor. But the stored price, the watch overview, {{restock.price}} / {{restock.previous_price}} in notifications, and the absolute price_change_min / price_change_max limits are all 100× too high.

Version
0.60.7 (code unchanged on master at d789fe3)

How did you install?
Docker

To Reproduce
Site-independent reproduction inside the container:

from changedetectionio.processors.restock_diff.processor import get_itemprop_availability
from changedetectionio.processors.restock_diff.pure_python_extractor import extract_metadata_pure_python, query_price_availability
from changedetectionio.processors.restock_diff import Restock

micro = '<div itemscope itemtype="http://schema.org/Product"><div itemprop="offers" itemscope itemtype="http://schema.org/Offer"><meta itemprop="priceCurrency" content="EUR"><meta itemprop="price" content="39,99"></div></div>'
ld = '<script type="application/ld+json">{"@context":"https://schema.org","@type":"Product","name":"x","offers":{"@type":"Offer","price":"39,99","priceCurrency":"EUR"}}</script>'

get_itemprop_availability(micro)['price']                              # 3999.0
get_itemprop_availability(ld)['price']                                 # 3999.0
query_price_availability(extract_metadata_pure_python(ld))['price']    # 3999.0
Restock().parse_currency("39,99")                                      # 39.99  <- correct

Example URL: https://store.steampowered.com/app/2129530/REANIMAL/?cc=nl

  • Restock & Price detection, plain requests fetcher. The page carries <meta itemprop="price" content="39,99"> with priceCurrency EUR, and the watch records 3999.0.
  • This title is age-gated, so the watch needs a request header Cookie: birthtime=631152001; lastagecheckage=1-0-1990; wants_mature_content=1; without it Steam redirects to /agecheck/.
  • Every EUR Steam region (?cc=nl, ?cc=ie, ?cc=de, …) formats the price the same way. Discussion Issue with thousands separator in prices #2599 (Steam, thousands separator) looks like the same root cause.

Expected behavior
39,99 is stored as 39.99.

Proposed fix
Let parse_currency() do the normalising instead of stripping first. That's a small change, done in #4494 with a regression test that fails on master and passes with the fix; the existing restock tests still pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions