From 00c6d0e039553d9837e820aecc2cf95d4c7ddc3e Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 7 Jun 2026 17:13:42 +0300 Subject: [PATCH] =?UTF-8?q?fix(sf):=20pravo.gov66=20law=5Ftype=3D12=20+=20?= =?UTF-8?q?/documents/=20=D1=81=D0=B5=D0=BB=D0=B5=D0=BA=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D1=8B=20(#1062)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/scrapers/pravo_gov66_client.py | 13 ++++++++++--- backend/tests/scrapers/test_pravo_gov66_client.py | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/backend/app/services/scrapers/pravo_gov66_client.py b/backend/app/services/scrapers/pravo_gov66_client.py index 872cdc20..a4a28da7 100644 --- a/backend/app/services/scrapers/pravo_gov66_client.py +++ b/backend/app/services/scrapers/pravo_gov66_client.py @@ -28,8 +28,12 @@ _SEARCH_URL = f"{_BASE_URL}/search/" # Тайм-аут щедрый: сервер иногда медленный. _TIMEOUT = httpx.Timeout(60.0, connect=15.0) +# Числовые коды типов документов (select#id_law_type на сайте). +# Текстовое значение «Постановление» — неверный параметр, сервер не матчит → 0 хитов. +_LAW_TYPE_POSTANOVLENIE = "1" # Постановление Правительства Свердловской области + # Паттерны для извлечения ссылок. -# Числовой doc_id в пути: /35496/ +# Числовой doc_id в пути: /35496/ (НЕ /documents/35496/ — у сайта такой структуры нет). _RE_DOC_PATH = re.compile(r"^/(\d+)/$") # PDF в /media/pravo/ (не ecp-вариант: ecp — подписанная копия, не всегда парсится). _RE_PDF_PATH = re.compile(r"/media/pravo/(?!ecp/)[^\s\"']+\.pdf", re.IGNORECASE) @@ -39,14 +43,16 @@ _RE_PDF_ECP_PATH = re.compile(r"/media/pravo/ecp/[^\s\"']+\.pdf", re.IGNORECASE) def search_documents( query: str, - law_type: str = "Постановление", + law_type: str = _LAW_TYPE_POSTANOVLENIE, since: str | None = None, ) -> list[dict[str, Any]]: """Поиск документов на pravo.gov66.ru. Args: query: поисковый запрос (2-3 слова; 1 или 4+ слов → 0 результатов на сервере). - law_type: тип документа, напр. «Постановление». + law_type: числовой код типа документа (см. select#id_law_type). По умолчанию + ``_LAW_TYPE_POSTANOVLENIE="1"`` (Постановление). НЕ передавать текст вроде + «Постановление» — сервер игнорирует нечисловые коды → 0 хитов. since: дата начала периода в формате «DD.MM.YYYY» (поле signed_start_date). Returns: @@ -161,6 +167,7 @@ def fetch_pdf(url: str) -> bytes: __all__ = [ + "_LAW_TYPE_POSTANOVLENIE", "fetch_pdf", "get_document_pdf_url", "search_documents", diff --git a/backend/tests/scrapers/test_pravo_gov66_client.py b/backend/tests/scrapers/test_pravo_gov66_client.py index 942edb63..cdb06a0a 100644 --- a/backend/tests/scrapers/test_pravo_gov66_client.py +++ b/backend/tests/scrapers/test_pravo_gov66_client.py @@ -7,6 +7,7 @@ from __future__ import annotations from app.services.scrapers.pravo_gov66_client import ( + _LAW_TYPE_POSTANOVLENIE, _extract_pdf_url, _parse_search_html, ) @@ -94,6 +95,13 @@ _DOC_HTML_NO_PDF = """ """ +class TestLawTypeConstant: + def test_postanovlenie_is_numeric_string(self) -> None: + """Числовой код — сервер игнорирует текстовые значения типа «Постановление».""" + assert _LAW_TYPE_POSTANOVLENIE == "1" + assert _LAW_TYPE_POSTANOVLENIE.isdigit() + + class TestParseSearchHtml: def test_extracts_doc_ids_and_titles(self) -> None: results = _parse_search_html(_SEARCH_HTML_FIXTURE)