Hide POIs, show overlay state, remove zoom button, rotate vpn

This commit is contained in:
Andras Schmelczer 2026-07-15 21:58:05 +01:00
parent 35276d34fa
commit bce34b73de
32 changed files with 1137 additions and 186 deletions

View file

@ -25,6 +25,32 @@ RETRY_BASE_DELAY = 2.0
# down if the portals start returning 429/403.
DETAIL_FETCH_CONCURRENCY = int(os.environ.get("DETAIL_FETCH_CONCURRENCY", "8"))
REQUESTS_PER_SECOND = float(os.environ.get("REQUESTS_PER_SECOND", "10"))
# Egress-block detection. A portal that 403s one URL is a quirk; a portal that
# 403s a whole HOST in a row is refusing our egress IP, which no amount of
# per-request retrying fixes. On 2026-07-15 Rightmove's Fastly edge did exactly
# this and every one of the 366 outcodes 403'd on the typeahead call, which the
# scraper silently recorded as "no such outcode" and published a Rightmove-less
# dataset. So: after this many consecutive 403s from one host, rotate the egress
# IP (finder/gluetun.py) and retry; once the rotation budget is spent and the
# host still 403s, raise http_client.BlockedError and fail the run loudly.
#
# Rotation is DISRUPTIVE (see gluetun.py: it restarts the tunnel shared with the
# media stack), so the budget is deliberately small. It is per-run, not per-host.
BLOCK_403_THRESHOLD = int(os.environ.get("BLOCK_403_THRESHOLD", "5"))
BLOCK_MAX_ROTATIONS = int(os.environ.get("BLOCK_MAX_ROTATIONS", "3"))
# Sanity gates on a finished scrape, checked before the parquet is overwritten
# (finder/scraper.py). A selected source yielding nothing, or the merged total
# collapsing against the previous run, means something is broken upstream rather
# than the market having moved; publishing that would quietly gut production.
#
# 10% is loose against real movement and tight against breakage: consecutive
# healthy cycles land within ~0.15% of each other (103,087 / 103,142 / 103,008),
# so a 10% fall is already a ~65x anomaly. It is deliberately BELOW the
# 2026-07-15 incident's own 18.7% drop (103,008 -> 83,785), which a 25% gate
# would have waved through.
MAX_ROW_DROP_RATIO = float(os.environ.get("MAX_ROW_DROP_RATIO", "0.10"))
GRID_CELL_SIZE = 0.01 # degrees for postcode spatial index
MAX_BEDROOMS = 20 # sanity cap: values above this are almost certainly parsing errors