CI поймал то, что статические проверки пропустили: пять строк получили
window_start_hour=23, window_end_hour=24, а констрейнт из 052_ требует
BETWEEN 0 AND 23. Весь INSERT падал, миграция не применялась.
Час 23 заворачивается в 0 — планировщик поддерживает окна через полночь
явно (window_end_hour <= window_start_hour → cross-midnight).
Тест test_migration_262_windows_are_one_hour и пропустил ошибку: он
требовал end == start + 1, что для 23 даёт ровно 24. Теперь сверяет
(start + 1) % 24, плюс отдельная проверка на диапазон 0..23 — то есть
на то самое условие прод-констрейнта.
Проверено сухим прогоном на боевой схеме: BEGIN → миграция → ROLLBACK,
psql exit 0, число строк не изменилось.
Покрытие было 6 городов: по области 3229 объявлений против 20111 по ЕКБ.
Заводим оставшиеся 40 городов области.
Координаты и идентификаторы площадок добыты, а не выведены по правилу.
Каждый метод сначала прогонялся на 5 городах с уже известными значениями и
только после точного воспроизведения применялся к новым:
- координаты — прод-геокодер, сверка с медианой сделок Росреестра по городу
- cian_id — geo-suggest, фильтр по «Свердловская область» (тёзки реальны:
Ревда в Мурманской, Заречный в Пензенской)
- yandex_rgid — region_suggest, дизамбигуация по label
- avito_slug — живой запрос страницы города; контроль: заведомо ложные
варианты дают чистый 404, то есть метод различает, а не угадывает
Сверка поймала три ошибки, которые иначе уехали бы в прод:
- Заречный: геокодер вернул микрорайон ВНУТРИ Екатеринбурга (обратное
геокодирование: «улица Софьи Перовской, Екатеринбург»)
- Сысерть: промах на ~23 км
- Бисерть: у Циана её нет, поиск отдаёт Сысерть — исключена целиком (пгт,
а не город области)
Строки заводятся ТОЛЬКО там, где идентификатор подтверждён: cian 40,
yandex 39 (нет Михайловска — его нет в гео-базе Яндекса вовсе), avito 23.
Итого 102 строки. Развёртка с неподтверждённым идентификатором молча
собирала бы Екатеринбург под меткой другого города — это хуже отсутствия.
- CityLocation: avito_slug/yandex_rgid стали Optional, cian_region_id
остался обязательным как единственный подтверждённый у всех
- явный ValueError до любого I/O, если город есть в CITY_LOCATIONS без
идентификатора: _city_seg() иначе откатывается на "ekaterinburg"
- CITY_DISPLAY_NAMES на 40 городов, иначе listings.city = «Екатеринбург»
- попутно: _OBLAST_AVITO_URL_PATTERNS падала на импорте при avito_slug=None
Все 102 строки выключены — включение волнами, вручную.