From 4902d1e29e02ae2a31e53c090949d4799d37b6f4 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 29 Aug 2026 15:59:54 +0300 Subject: [PATCH] =?UTF-8?q?chore(tradein):=20=D1=87=D0=B5=D1=82=D1=8B?= =?UTF-8?q?=D1=80=D0=B5=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE=D0=BB=D0=B0,=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BD=D0=B5=D1=82=20=D0=BD=D0=B8=20=D0=BE=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Найдено сверкой двух независимых источников: подсчёт упоминаний по всем python-файлам (включая тесты) дал имена, встречающиеся ровно один раз — в собственном объявлении; каждое затем подтверждено serena find_referencing_symbols (LSP, видит и косвенные ссылки) и проверено grep'ом по yml/sql/ts/md на случай ссылки строкой. Удалено: * _qr_code_data_url (trade_in_pdf) — QR как SVG data URL, не вызывался ниоткуда; вместе с ним ушёл осиротевший import segno. `io` ОСТАВЛЕН — io.BytesIO используется дальше по файлу (строка 774); * _local_name (gar_flats_loader) — снятие namespace с имени тега; * AvitoParseError — класс никто не поднимает и не ловит; * IMVCityMismatchError — то же. Что НЕ тронуто, хотя фильтр их показал: * ~200 обработчиков FastAPI и celery-задач — их поднимает декоратор, по имени их действительно никто не зовёт; * refresh_ddu_price_indicator — точка ручного обслуживания, задокументирована в комментарии к матвью (data/sql/152_mv_ddu_price_indicator.sql: «Refresh: ... (не в beat)»); * schemas/parcel.py::MarketPrice — половина контракта, фронт использует (frontend/src/types/site-finder.ts: market_price?: MarketPrice); * JobSetting — SQLAlchemy-модель, живёт через metadata Base. segno остался в backend/pyproject.toml и больше нигде не используется — снятие зависимости требует пересборки лока, поэтому отдельным PR. ruff clean, 4974 passed / 37 skipped. --- .../backend/app/services/exporters/trade_in_pdf.py | 9 --------- tradein-mvp/backend/app/services/gar_flats_loader.py | 7 ------- .../scraper-kit/src/scraper_kit/avito_exceptions.py | 4 ---- .../scraper-kit/src/scraper_kit/providers/avito/imv.py | 4 ---- 4 files changed, 24 deletions(-) diff --git a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py index ca2aff59..341fea48 100644 --- a/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py +++ b/tradein-mvp/backend/app/services/exporters/trade_in_pdf.py @@ -46,7 +46,6 @@ import matplotlib matplotlib.use("Agg") # headless-safe backend — ОБЯЗАТЕЛЬНО до любого рендера: в Docker-образе # нет GUI toolkit (Tkinter/Qt/GTK), импорт matplotlib без явного backend может тянуть их/падать. import matplotlib.font_manager as _font_manager -import segno from matplotlib.figure import Figure # object API, НЕ pyplot — см. _price_range_chart_svg from matplotlib.patches import Rectangle @@ -441,14 +440,6 @@ def _expires_date(estimate: AggregatedEstimate) -> dt.date: return dt.date.today() + dt.timedelta(days=30) -def _qr_code_data_url(text: str, size: int = 4) -> str: - """QR-код как SVG data URL.""" - qr = segno.make(text, error="m") - buf = io.BytesIO() - qr.save(buf, kind="svg", scale=size, dark="#1a1d23", light="#ffffff") - return f"data:image/svg+xml;base64,{base64.b64encode(buf.getvalue()).decode('ascii')}" - - def _conf_label(confidence: str) -> str: return {"high": "Высокая", "medium": "Средняя", "low": "Низкая"}.get(confidence, confidence) diff --git a/tradein-mvp/backend/app/services/gar_flats_loader.py b/tradein-mvp/backend/app/services/gar_flats_loader.py index fd3c4538..5b978515 100644 --- a/tradein-mvp/backend/app/services/gar_flats_loader.py +++ b/tradein-mvp/backend/app/services/gar_flats_loader.py @@ -141,13 +141,6 @@ def _find_files(dir_path: str, prefix: str) -> list[str]: # ───────────────────────────────────────────────────────────────────────────── # Стриминговый парсер (lxml iterparse + очистка) # ───────────────────────────────────────────────────────────────────────────── -def _local_name(tag: object) -> str: - """Локальное имя тега без namespace; '' для не-элементов (комментарии/PI).""" - if not isinstance(tag, str): - return "" - return tag.rsplit("}", 1)[-1] - - def _stream_rows(path: str, marker_attr: str) -> Iterator[etree._Element]: """Yield row-элементов файла, у которых есть атрибут marker_attr, освобождая память. diff --git a/tradein-mvp/packages/scraper-kit/src/scraper_kit/avito_exceptions.py b/tradein-mvp/packages/scraper-kit/src/scraper_kit/avito_exceptions.py index 392d8033..cae75644 100644 --- a/tradein-mvp/packages/scraper-kit/src/scraper_kit/avito_exceptions.py +++ b/tradein-mvp/packages/scraper-kit/src/scraper_kit/avito_exceptions.py @@ -71,7 +71,3 @@ class AvitoWarmupCookiesMissingError(AvitoBlockedError): proxy-ротация, consecutive-block breaker в pipeline/backfill) отработали как на обычном блоке площадки, без отдельного except-блока на call-сайтах. """ - - -class AvitoParseError(AvitoError): - """Cannot parse Avito HTML structure (selector changes).""" diff --git a/tradein-mvp/packages/scraper-kit/src/scraper_kit/providers/avito/imv.py b/tradein-mvp/packages/scraper-kit/src/scraper_kit/providers/avito/imv.py index 9c84e2a2..e36818f8 100644 --- a/tradein-mvp/packages/scraper-kit/src/scraper_kit/providers/avito/imv.py +++ b/tradein-mvp/packages/scraper-kit/src/scraper_kit/providers/avito/imv.py @@ -92,10 +92,6 @@ class IMVAddressNotFoundError(Exception): """Raised when /coords/by_address returns no geoHash for given address.""" -class IMVCityMismatchError(Exception): - """Raised when locationId не входит в ожидаемый город (sanity check).""" - - class IMVAuthError(Exception): """Avito API rejected auth / quota exceeded (HTTP 401 / 403).""" -- 2.45.3