13 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| c927b77777 |
fix(tradein/imv): «временная» ошибка снова временная — 1390 домов возвращаются в очередь (#2843)
Some checks failed
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Failing after 3m29s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Has been skipped
|
|||
| c66e18cd13 |
fix(tradein/imv): домовая оценка ходит в сайдкар через пул, а не через чужой env-прокси (#2698) (#2708)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m0s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 1m38s
|
|||
| 43aaf91b97 |
fix(tradein): писатели наконец пишут то, что обещает схема — фото подсказок, статус «снято», события объявлений (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m56s
Три находки одного класса из эпика: колонка есть, писатель есть, тест на писателя зелёный — а данные не появляются. Тестами это не ловится по построению, только сверкой с продом. 1. house_suggestions: парсер выбрасывал imageLink, а INSERT не перечислял image_link + area_m2/rooms/floor/total_floors. 25 055 строк с NULL во всех пяти колонках, ~74 дня с миграции 064. Метрики парсятся из title тем же _parse_title, что и у placementHistory. 2. listings_snapshots.status: 'active' у всех 394 299 строк при 55 448 реально неактивных объявлений. Оба места вызова с литералом 'active' честны — там объявление действительно видели; не писал никто ветку «снято». Теперь оба места деактивации пишут снимок 'closed' в ТОЙ ЖЕ транзакции: TTL-задача (data-modifying CTE, все 4 источника через один deactivate_stale_listings) и 404 из avito_detail_backfill. Дата снятия перестаёт быть догадкой. 3. listing_source_events: схема знает 5 типов, писался 1 (price_change, 8288 строк). Дописаны ветки delisted/relisted/edited/first_seen в тот же set-based statement — данные для них уже лежат в снимке. JOIN → LEFT JOIN LATERAL, иначе first_seen недостижим по построению; план #2607 (per-row index point-lookup по idx_lss_source_date) сохранён, проверено EXPLAIN на проде. Счётчики прогона теперь по типам, все пять всегда присутствуют — ровно они показали бы четыре нуля из пяти. Миграция не нужна: все колонки и CHECK уже существуют. Тесты: tests/test_2674_writers_honor_schema.py. Гейты сверяют писателя со СХЕМОЙ (колонки INSERT против CREATE TABLE 064, типы событий против CHECK 079), поэтому ловят и следующую забытую колонку. Фальсификация патч-методом: без фикса 1 — 6 красных, без фикса 2 — 6, без фикса 3 — 4. |
|||
| 4b4ab8b34c |
fix(tradein/imv): счётчики прогона в total_seen/new_count + лог дрейфа ремонта (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m51s
По ревью PR #2675. 1. counters прогона не заполняли выделенные колонки. _column_counts (scrape_runs.py) берёт total_seen из ключей total_seen|lots_fetched и new_count из new_count|lots_inserted — ни одного из них в дикте не было, поэтому все 39 прогонов этого source лежат в БД с total_seen=0. А mark_done по этой же колонке шлёт алерт «3 подряд done с нулевым результатом» (#2625): даже идеальный прогон с 50 сохранёнными считался бы нулевым и через три дня выстрелил бы ложной тревогой про капчу. Добавлены total_seen=checked и new_count=saved. Трейд-офф назван в комментарии: на исчерпанной очереди checked=0 три дня подряд тоже даст алерт — но пустая очередь при ежедневном расписании это и правда сигнал. 2. _map_renovation_type молча схлопывал в 'cosmetic' любое незнакомое непустое значение. Сегодня в проде ровно четыре канонических, живого эффекта нет, но дрейф вокабуляра реален (70950 строк listings с пустым нормализованным ремонтом). Добавлен logger.debug на случай «непустое, но не в карте» — паритет с house_type_normalizer, который такой лог уже пишет. 3. Обоснование дефолта 'cosmetic' в докстринге заменено на более сильное по данным: это одновременно МОДА и МЕДИАННАЯ категория популяции (standard 7984 / good 7116 / needs_repair 4738 / excellent 2562; кумулятивно needs_repair 21.2%, +standard 56.8%), то есть наилучшая одиночная догадка, а не просто «не край шкалы». Там же названа асимметрия: поштучный путь эстиматора при неизвестном ремонте IMV вообще не зовёт, а домовой дефолтит — решение осознанное (иначе теряем ещё ~32% домов очереди), чтобы следующий читатель не принял это за недосмотр. Refs #2674 |
|||
| 0815319e1c |
fix(tradein/imv): домовая оценка перестаёт врать про ремонт и тип дома (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m51s
Три дефекта в house_imv_backfill, найденные системным поиском (эпик #2674/#2673).
1. Тип ремонта был захардкожен литералом 'cosmetic' — все 2685 запросов ушли
как «косметический ремонт», хотя мода repair_state по объявлениям тех же
домов другая: standard 4564 / good 4118 / needs_repair 2279 / excellent 1631
(косметика лишь 36%). Теперь renovation_type берётся из mode(repair_state)
в том же агрегате, что уже считает медианы комнат/площади/этажа, и проходит
через существующий estimator._IMV_REPAIR_MAP (ленивый импорт — estimator
тянет scraper_adapters, а тот импортирует этот модуль). Второго словаря не
заводим. Неизвестный ремонт (498 домов из 2685) остаётся 'cosmetic': это
середина порядковой шкалы required < cosmetic < euro < designer, а не край,
системного сдвига в одну сторону не даёт.
2. Неизвестный тип дома молча становился 'panel' — и когда типа нет вовсе, и
когда он есть, но не совпал со словарём. Панель почти самый дешёвый класс
(медиана по нашим же 2685 оценкам: block 122.6k < panel 128.8k <
brick 131.1k < monolithic 145.9k руб/м2), то есть дефолт систематически
занижал. На проде так уехали 363 дома совсем без типа и 75 домов с
camelCase-типом из Циана (56 из них monolithBrick — минус 11.7% против
monolithic). Теперь сырое значение прогоняется через общий
scraper_kit.house_type_normalizer.normalize_house_type (знает monolithBrick /
gasSilicateBlock / aerocreteBlock / stalin и SCREAMING-вокабуляр Яндекса),
дефолт 'panel' убран: тип не распознан → house_type=None → дом помечается
no_params ('unknown house_type') и запрос к площадке не тратится. 'other' и
'wireframe' намеренно НЕ маппятся — честного соответствия у них нет.
3. Прогон не умел падать: 31 прогон подряд с saved=0 и ~35 ошибками из 50
помечен 'done'. Тот же класс, что #2670/#2657 — успех определялся как «не
поймали известное исключение». Теперь saved=0 при errors>0 → mark_failed.
Ноль сохранённых БЕЗ ошибок (всё отфильтровано в skipped) остаётся done.
Балкон/лоджия оставлены константами намеренно: покрытие listings.has_balcony
13.8%, listings.balcony_loggia 9.4%, и колонки противоречат друг другу (по
has_balcony «есть» у 62%, а по balcony_loggia самый частый случай — loggia
5650 против balcony 2794). Мода по одному-двум объявлениям на таком покрытии —
шум, а не данные.
Причина, по которой бэкфилл не сохранил НИ ОДНОЙ оценки за 34 дня, — вне этого
модуля и здесь не чинится (детали и числа в описании PR): 1240 домов легли на
отказе браузерного сайдкара «нет прокси» (гейт #2616, 05.07-02.08), а после
возврата прокси 05.08 — 23 на Page.evaluate «Execution context was destroyed»
в tradein-browser и 12 на 403 Авито.
Refs #2674
|
|||
|
|
79a77200d7 |
refactor(tradein/scrapers): удалить legacy scrape_pipeline.py + тест-хирургия (#2397 Part E1)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m43s
app.services.scrape_pipeline (3734 строк) имел 0 runtime-импортёров — Part C убрал
последнего вызывающего (admin.py, scheduler_main.py уже на
scraper_kit.orchestration.pipeline). Удаление:
- backend/app/services/scrape_pipeline.py — удалён целиком
Тесты (18 файлов, импортировавших scrape_pipeline):
DELETE (чистая legacy-оркестрация, kit-эквивалент покрыт в
test_scraper_kit_pipeline_parity{,2}.py):
- test_scrape_pipeline.py, test_scrape_pipeline_proxy_pool.py (proxy-pool DB-lease
вытеснен RealProxyProvider DI-адаптером, покрытым test_scraper_adapters_contracts.py)
- test_anchor_watchdog.py, test_ban_rotation_1790.py, test_provider_rotation_1848.py
- test_scraper_resilience_1949_1950.py, test_sweep_drain.py
CONVERT (parity-сравнение убрано, kit-only regression оставлен/усилен):
- test_scraper_kit_pipeline_parity.py, test_scraper_kit_pipeline_parity2.py —
_drive_old убран, _drive_new стал единственной веткой; ассёрты на конкретные
значения counters сохранены (это теперь основное regression-покрытие kit
orchestration)
- test_sweep_imv_phase.py — 6 IMV-phase тестов (enrich_imv true/false/no-touched/
crash/failed-counter/cancel) переведены на run_avito_city_sweep (kit) с DI
(config/matcher/enrichment); enrichment.process_houses_imv_batch — инжектируемый
Protocol-метод вместо прямого импорта app.services.house_imv_backfill
PARTIAL-EDIT (легаси sweep-behavior вырезан, admin/kit-DI/несвязанные тесты
оставлены):
- test_city_sweep.py — только retarget 4 импортов на scraper_kit.orchestration.pipeline
- test_cian_city_sweep.py, test_yandex_city_sweep.py — sweep-behavior секции удалены,
admin endpoint тесты (уже kit-DI) + anchor-timeout/watchdog формулы (retarget import)
оставлены
- test_domclick_sweep.py — sweep-wiring тесты удалены, Counters + _map_item оставлены
- test_avito_newbuilding_sweep.py — sweep-wiring тест #5 удалён, retarget
NewbuildingSweepCounters
- test_pipeline_browser_routing.py — 3 run_avito_pipeline теста удалены, browser-routing
на уровне fetch_detail/fetch_house_catalog/AvitoScraper не тронут
- test_scraper_proxy.py, tests/scrapers/test_avito_anti_bot.py — _avito_proxies()
переведён на kit (DI-параметр вместо settings-patch); pipeline-level тесты удалены
Stale-комментарии (scrape_pipeline.py как текущая реальность) поправлены в
admin.py, avito_detail_backfill.py, house_imv_backfill.py, yandex_price_history.py,
scrapers/avito_detail.py + kit-пакете (orchestration/__init__.py, orchestration
/pipeline.py, providers/avito/detail.py — strangler завершён, легаси удалён).
grep -rn scrape_pipeline app/ scripts/ packages/ — только past-tense упоминания
("удалён в Part E1", "перед его удалением") + один historical footnote
(contracts.py:138, provenance-заметка "собраны grep'ом по ... .py").
Backend suite: 3088 passed, 6 skipped (известный flake test_search_cache_hit #2208 —
не регрессия). ruff check чист на всех изменённых файлах.
|
||
|
|
4a6820b8f7 |
feat(tradein/estimator): switch core valuation call sites to scraper_kit (#2337)
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m48s
Group E4 (final, highest-risk step of scraper_kit migration epic #2277): estimator.py and house_imv_backfill.py's avito_imv/cian_valuation/ yandex_valuation call sites now import from scraper_kit.providers.* instead of app.services.scrapers.*, following the exact wiring proven safe by E1/E2/E3 (#2334/#2335/#2336): - estimator.py's avito IMV (_get_or_fetch_imv_cached, both call sites) and house_imv_backfill.py's _process_one_house: add config=RealScraperConfig() — kit's evaluate_via_imv silently drops the configured proxy without it. - estimator.py's cian valuation (Stage 9): add config=RealScraperConfig() — mandatory kwarg on the kit function (TypeError if omitted). - estimator.py's yandex valuation: add config=RealScraperConfig() (mandatory) and delay_provider=get_scraper_delay — without it kit silently falls back to a hardcoded 5.0s throttle instead of the DB-configured anti-ban delay. - All exception classes imported consistently from the same kit module as evaluate_via_imv (not just the function) — mixing legacy/kit exception classes would break `except IMVAddressNotFoundError` etc. via identity mismatch (caught by an existing test that assumed the legacy class, fixed alongside). Observability: both cian_valuation and yandex_valuation graceful-degradation except-blocks upgraded from logger.warning to logger.exception. GlitchTip's LoggingIntegration listens at event_level=ERROR (main.py/scheduler_main.py) — a WARNING never reaches GlitchTip as an event regardless of exc_info, so a future config-wiring mistake at these call sites needs ERROR level to be visible in monitoring. house_imv_backfill.py: RealScraperConfig is imported lazily inside _process_one_house (not at module level) to avoid a circular import — app.services.scraper_adapters imports backfill_house_imv/ process_houses_imv_batch from this module at module level. Verified via direct import in both orders plus a full `app.main` import. Also fixes a stale docstring claiming process_houses_imv_batch is "not wired into scheduler" — it is, via scrape_pipeline.py's run_avito_city_sweep. Test updates: 2 pre-existing tests (test_backfill_wave2.py) mocked the legacy IMVAddressNotFoundError/IMVEvaluation/IMVGeo classes, now updated to import from scraper_kit to match the production exception identity. Added config=/delay_provider= regression-guard asserts to the relevant estimator and backfill tests, mirroring the existing #2306 cian_price_history pattern. Legacy app/services/scrapers/{avito_imv,cian_valuation,yandex_valuation}.py are untouched and still imported by cian_history_backfill.py's valuation block (separately scoped, not touched here) — revert is a clean single-commit revert, no schema/data migration involved. |
||
|
|
eb564cfc01 |
feat(tradein/scraper-kit): migrate backfill-task imports to kit, Group C (#2310)
Migrates legacy app.services.scrapers.* imports to scraper_kit equivalents for house_imv_backfill.py, avito_detail_backfill.py, cian_history_backfill.py, ekb_geoportal_ingest.py, and yandex_detail_backfill.py, proving parity via tests/support/parity.assert_parity per the epic's gate (#2304). newbuilding_enrich_backfill.py and yandex_newbuilding_sweep.py are left fully on legacy imports: both call into scraper_kit.providers.{cian,yandex}.newbuilding, which construct BrowserFetcher(source=...) without the now-mandatory endpoint= kwarg (issue #2322, verified still open against the actual provider source, not just issue status) -- no caller-side fix is possible, matching Group A's (#2305) precedent for the same bug in admin.py. Config-gated kit function footguns found and fixed (Group B #2306 pattern): - avito fetch_detail's backconnect-on-403 retry silently drops when config= is omitted -- now passes config=RealScraperConfig() explicitly, with a regression test proving the gate. - kit AvitoScraper's constructor now requires ScraperConfig positionally -- wired via RealScraperConfig(), which _rotate_ip() reads for avito_proxy_rotate_url. - BrowserFetcher(source=...) call sites (house_imv_backfill, avito/cian detail backfills) now pass the mandatory endpoint=settings.browser_http_endpoint. New footgun discovered (NOT #2322, flagged for follow-up): kit's build_warmed_session() builds its curl_cffi session via _build_detail_session() with no config parameter at all, unlike fetch_detail -- migrating it would silently drop the sticky MGTS-proxy egress on avito_detail_backfill's warm-batch path (the prod default). Left build_warmed_session/ _AVITO_WARM_SEARCH_URL on legacy imports, documented inline. Refs #2310 |
||
| e03c810306 |
feat(tradein-imv): route IMV backfill через BrowserFetcher.fetch_json за dormant-флагом (#915 Stage 3) (#1916)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 27s
Deploy Trade-In / test (push) Successful in 1m23s
Deploy Trade-In / build-backend (push) Successful in 51s
Deploy Trade-In / deploy (push) Successful in 46s
|
|||
| 8279b0ee1f |
fix(week-review): cross-file добивка код-ревью (#1338, #1363, #1419, #1421, #1424, #1480)
- #1338 concepts.py: geometry.generate вынесен в run_in_threadpool (не блокирует event loop) - #1363 house_imv_backfill.py + scrape_pipeline.py: периодический heartbeat в долгих IMV-фазах (reap_zombies не помечает живой sweep zombie) - #1419 site-finder-api.ts: placeholderData keepPreviousData для bbox-запроса (KPI не мигает «0/0» при пане/зуме) - #1421 site-finder-api.ts: area_ha nullable + адаптер не подставляет 0; null-гейт потребителей EntryMap.tsx, ParcelDrawer.tsx - #1424/#1480 EstimateResult.tsx: блок «Параметры объекта» скрыт при восстановлении по shared-ссылке (нет мусора «0 м²/Студия/Этаж 0 из 0») Верификация: tsc --noEmit 0 ошибок; py_compile OK. |
|||
| 86e9ea2937 |
fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1»)
Многоагентный аудит + имплементация: один воркер на файл, точечные правки. Верификация: py_compile (47/47 .py) + tsc --noEmit (0 ошибок). Unit-тесты не прогонялись (окружение не поднято: rollup native dep / нет pytest-venv). Полностью исправлено (169): #1336, #1337, #1339, #1340, #1341, #1342, #1343, #1345, #1346, #1348, #1349, #1350, #1351, #1354, #1356, #1358, #1359, #1360, #1362, #1364, #1365, #1366, #1367, #1368, #1369, #1370, #1371, #1372, #1373, #1374, #1375, #1376, #1377, #1378, #1379, #1380, #1381, #1382, #1384, #1385, #1386, #1387, #1388, #1389, #1390, #1391, #1392, #1394, #1395, #1396, #1397, #1399, #1400, #1401, #1402, #1403, #1404, #1408, #1409, #1410, #1411, #1412, #1413, #1414, #1415, #1416, #1417, #1418, #1420, #1423, #1425, #1426, #1427, #1428, #1429, #1430, #1431, #1432, #1433, #1434, #1435, #1437, #1438, #1439, #1440, #1441, #1442, #1443, #1444, #1445, #1446, #1447, #1448, #1449, #1450, #1451, #1452, #1453, #1454, #1455, #1456, #1457, #1458, #1459, #1460, #1461, #1462, #1463, #1464, #1465, #1466, #1467, #1468, #1469, #1471, #1472, #1473, #1474, #1476, #1478, #1479, #1481, #1482, #1483, #1484, #1485, #1487, #1488, #1489, #1490, #1491, #1492, #1493, #1494, #1495, #1496, #1497, #1499, #1500, #1501, #1502, #1504, #1505, #1506, #1507, #1510, #1514, #1515, #1516, #1517, #1518, #1519, #1521, #1522, #1523, #1524, #1525, #1526, #1527, #1528, #1529, #1531, #1532, #1533, #1534, #1535, #1536, #1537, #1538 Частично (9, in-file часть, остаток cross-file): #1361, #1419, #1422, #1424, #1470, #1475, #1477, #1480, #1498 Требуют cross-file (3, не тронуты): #1338, #1363, #1421 Пропущено (1): #1539 Не входило в партию: 22 needs-Leha issue (нужны решения владельца). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
|||
| d78ae9f649 |
feat(tradein): avito city-sweep — IMV-фаза (один прогон = SERP+detail+houses+IMV) (#861)
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 29s
Deploy Trade-In / build-backend (push) Successful in 40s
Deploy Trade-In / deploy (push) Successful in 34s
Co-authored-by: bot-backend <bot-backend@gendsgn.local> Co-committed-by: bot-backend <bot-backend@gendsgn.local> |
|||
| 5373f5db63 |
feat(tradein): admin backfill endpoints — cian price-history (T8a) + yandex address (T10) + house IMV (T7) (#856)
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 28s
Deploy Trade-In / build-backend (push) Successful in 39s
Deploy Trade-In / deploy (push) Successful in 34s
Co-authored-by: bot-backend <bot-backend@gendsgn.local> Co-committed-by: bot-backend <bot-backend@gendsgn.local> |