price-list-normalizer.zip — 68,808 bytes, 38 files
sha256:1e33c317ebe689a9118fbda30e522efd3678b0ad6c65101bed26746732462450
Extract and run
python3 tests/test_review_cases.py, python3 tests/test_parser.py and
python3 tests/test_v3_regressions.py from the extracted directory.
No token, account or network needed.
| Suite | Result |
|---|---|
| s01-s11 review layouts (yours) | 34/34 |
| test_parser.py (mine) | 31/31 |
| test_v3_regressions.py (yours, 14 checks) | 14/14 — v3 scored 6/14 |
| n01-n10 new layouts | all parse |
The magnitude suffix I added in v3 matched the first letter of the NEXT WORD, so a bare number followed by a word starting with k, M or m became a magnitude:
| Input | v3 (wrong) | v4 |
|---|---|---|
| Crew of 2-3 movers $120/hr | range 2–3000000 | $120/hr |
| Chain-link fence 10m run $450 | 10,000,000 | $450 |
| 4K TV wall mounting $149 | 4000 | $149 |
| Tree removal 20-30 ft $450 | range 20–30 | $450 |
A magnitude suffix may no longer be followed by a letter, and it requires a currency
symbol — 10m run is a length, not ten million.
A range also always beat a real price. A range that is not itself money and has a
currency-marked figure outside it is a quantity or a size, not a price: the
currency-marked figure now wins and the range is kept as range_present.
Descriptions only strip a stranded symbol, so minimum $85 keeps its $.
Schemas validate; _NoRedirect forces every redirect hop through validation
(11/11 probes blocked); billing only on rows carrying a price, range or "call for price";
LLM event gated on APIFY_USER_IS_PAYING.
$0.20 per price list, up to 1,000 rows. Per list, not per row — a price list is one job whether it has fifteen rows or nine hundred.
Nothing is deployed. Publishing, ID check and payout stay with John and Goji.