feat(tradein): geocode-missing для deals (#4 завершение)
geocode-missing получил target=listings|deals. deals.geocode_tried_at добавлен в миграцию 005. cron геокодит и сделки Росреестра по улицам.
This commit is contained in:
parent
9ac4f35abc
commit
4ea35d5740
3 changed files with 40 additions and 17 deletions
|
|
@ -140,28 +140,35 @@ def _clean_address_for_geocode(addr: str) -> str:
|
|||
async def geocode_missing(
|
||||
db: Annotated[Session, Depends(get_db)],
|
||||
limit: int = 100,
|
||||
target: Literal["listings", "deals"] = "listings",
|
||||
) -> dict:
|
||||
"""Геокодинг listings у которых нет lat/lon (используя address).
|
||||
"""Геокодинг listings ИЛИ deals у которых нет lat/lon (используя address).
|
||||
|
||||
target=listings (по умолч.) — объявления Cian/N1; target=deals — сделки Росреестра.
|
||||
Чанк-обработка: limit=100 → ~150-200с (Nominatim 1 req/sec). cron вызывает
|
||||
в цикле пока `remaining` > 0.
|
||||
|
||||
geocode_tried_at: после КАЖДОЙ попытки (успех/провал) ставим NOW(). Failed-
|
||||
адреса не выбираются повторно 7 дней → cron-loop завершается, не зацикливается.
|
||||
geom обновляется автоматически триггером listings_set_geom_trg.
|
||||
geom обновляется автоматически триггером.
|
||||
"""
|
||||
# Доп. фильтр для listings — у Avito/N1 встречаются плейсхолдер-адреса.
|
||||
extra_filter = (
|
||||
"AND address NOT LIKE '%(Avito)%' AND address NOT LIKE '%(N1)%'"
|
||||
if target == "listings"
|
||||
else ""
|
||||
)
|
||||
rows = db.execute(
|
||||
text(
|
||||
"""
|
||||
f"""
|
||||
SELECT id, address
|
||||
FROM listings
|
||||
FROM {target}
|
||||
WHERE lat IS NULL
|
||||
AND COALESCE(address, '') != ''
|
||||
AND address NOT LIKE '%(Avito)%'
|
||||
AND address NOT LIKE '%(N1)%'
|
||||
{extra_filter}
|
||||
AND (geocode_tried_at IS NULL
|
||||
OR geocode_tried_at < NOW() - interval '7 days')
|
||||
ORDER BY geocode_tried_at NULLS FIRST, scraped_at DESC
|
||||
ORDER BY geocode_tried_at NULLS FIRST
|
||||
LIMIT :limit
|
||||
"""
|
||||
),
|
||||
|
|
@ -176,7 +183,7 @@ async def geocode_missing(
|
|||
if result is None:
|
||||
# Помечаем что пробовали — иначе ретрай на каждом cron.
|
||||
db.execute(
|
||||
text("UPDATE listings SET geocode_tried_at = NOW() WHERE id = :id"),
|
||||
text(f"UPDATE {target} SET geocode_tried_at = NOW() WHERE id = :id"),
|
||||
{"id": row["id"]},
|
||||
)
|
||||
db.commit()
|
||||
|
|
@ -185,7 +192,7 @@ async def geocode_missing(
|
|||
# geom пересчитается триггером из lat/lon.
|
||||
db.execute(
|
||||
text(
|
||||
"UPDATE listings SET lat = :lat, lon = :lon, "
|
||||
f"UPDATE {target} SET lat = :lat, lon = :lon, "
|
||||
"geocode_tried_at = NOW() WHERE id = :id"
|
||||
),
|
||||
{"lat": result.lat, "lon": result.lon, "id": row["id"]},
|
||||
|
|
@ -196,12 +203,11 @@ async def geocode_missing(
|
|||
# Сколько ещё не-геокоженных адресов ждут (для cron-loop'а).
|
||||
remaining = db.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT count(*) FROM listings
|
||||
f"""
|
||||
SELECT count(*) FROM {target}
|
||||
WHERE lat IS NULL
|
||||
AND COALESCE(address, '') != ''
|
||||
AND address NOT LIKE '%(Avito)%'
|
||||
AND address NOT LIKE '%(N1)%'
|
||||
{extra_filter}
|
||||
AND (geocode_tried_at IS NULL
|
||||
OR geocode_tried_at < NOW() - interval '7 days')
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,8 +11,12 @@
|
|||
-- Идемпотентно (ADD COLUMN IF NOT EXISTS) — безопасно при каждом деплое.
|
||||
|
||||
ALTER TABLE listings ADD COLUMN IF NOT EXISTS geocode_tried_at timestamptz;
|
||||
ALTER TABLE deals ADD COLUMN IF NOT EXISTS geocode_tried_at timestamptz;
|
||||
|
||||
-- Частичный индекс: ускоряет выборку «что ещё геокодить».
|
||||
-- Частичные индексы: ускоряют выборку «что ещё геокодить».
|
||||
CREATE INDEX IF NOT EXISTS listings_geocode_pending_idx
|
||||
ON listings (geocode_tried_at NULLS FIRST)
|
||||
WHERE lat IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS deals_geocode_pending_idx
|
||||
ON deals (geocode_tried_at NULLS FIRST)
|
||||
WHERE lat IS NULL;
|
||||
|
|
|
|||
|
|
@ -71,13 +71,26 @@ done
|
|||
# Геокодинг свежих лотов без координат (Cian/N1 отдают только адрес).
|
||||
# Чанками по 100 (~200с/чанк, под таймаутом), пока remaining > 0.
|
||||
# Макс 15 чанков = 1500 адресов. geocode_tried_at не даёт зациклиться.
|
||||
log "→ geocode-missing (чанками)"
|
||||
log "→ geocode-missing listings (чанками)"
|
||||
for gi in $(seq 1 15); do
|
||||
geo=$(docker exec "$CONTAINER" curl -s -X POST \
|
||||
"http://localhost:8000/api/v1/admin/geocode-missing?limit=100" \
|
||||
"http://localhost:8000/api/v1/admin/geocode-missing?limit=100&target=listings" \
|
||||
-H "$ADMIN_HDR" \
|
||||
-m 320 2>/dev/null || echo '{"error":"geocode_timeout","remaining":0}')
|
||||
log "← geocode чанк $gi: $geo"
|
||||
log "← listings чанк $gi: $geo"
|
||||
remaining=$(echo "$geo" | grep -oE '"remaining":[0-9]+' | grep -oE '[0-9]+' || echo 0)
|
||||
[ "${remaining:-0}" -eq 0 ] && break
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Сделки Росреестра тоже без координат (импорт отдаёт только улицу).
|
||||
log "→ geocode-missing deals (чанками)"
|
||||
for gi in $(seq 1 30); do
|
||||
geo=$(docker exec "$CONTAINER" curl -s -X POST \
|
||||
"http://localhost:8000/api/v1/admin/geocode-missing?limit=100&target=deals" \
|
||||
-H "$ADMIN_HDR" \
|
||||
-m 320 2>/dev/null || echo '{"error":"geocode_timeout","remaining":0}')
|
||||
log "← deals чанк $gi: $geo"
|
||||
remaining=$(echo "$geo" | grep -oE '"remaining":[0-9]+' | grep -oE '[0-9]+' || echo 0)
|
||||
[ "${remaining:-0}" -eq 0 ] && break
|
||||
sleep 2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue