fix(ptica): тип постановления — по первому упоминанию, а не по порядку проверок (#2464) #2980

Merged
bot-backend merged 1 commit from fix/2464-detect-kind into main 2026-08-20 16:28:28 +00:00
Collaborator

Дефект

_detect_kind проверял «резервир» первым и возвращал резервирование безусловно:

if _RE_REZERV.search(text):   return "резервирование"
if _RE_IZYAT.search(text):    return "изъятие"

Постановление об изъятии, где резервирование упомянуто вскользь — типовая формулировка «ранее зарезервированных земель», ссылка на утративший силу акт о резервировании — классифицируется как резервирование.

Ошибка не единичная. kind в extract_reservations вычисляется один раз на весь документ и проставляется каждой записи:

kind = _detect_kind(text, default_kind)
for m in _RE_CAD_NUM.finditer(text):
    records.append(ReservationRecord(cad_num=cad, reservation_kind=kind, ...))

Один документ с 40 участками → 40 неверных строк в land_reservation.

Правка

Побеждает то слово, что встретилось раньше. Тема документа стоит в заголовке, поэтому позиция — сигнал сильнее порядка проверок.

Важно, что решение симметрично: заголовок «О резервировании» так же выигрывает у «изъятия» в теле («зарезервированные участки не подлежат изъятию»). Простая смена порядка проверок дала бы обратный перекос — на это поставлен отдельный контроль.

Текущих ошибок на проде нет — измерено

Уточняю первую редакцию этого описания: 27 строк в land_reservation не проходили через этот парсер. Они из другой трубы.

land_reservation по источникам:
  izyatie_ekb_ocr   27   ← izyatie_ocr.py, OCR со страниц екатеринбург.рф
  page_pdf           0   ← page_reservation_parser (этот PR)
  pravo_gov66        0   ← page_reservation_parser (этот PR)

izyatie_ocr.py вообще не зовёт _detect_kind — он ставит reservation_kind = "изъятие" константой. То есть парсер из этого PR не записал на прод ни одной строки, и текущей порчи от дефекта нет по той же причине, по какой нет и данных.

Правка закрывает возможность, а не чинит существующую порчу — говорю это прямо, чтобы никто не записал сюда прод-победу.

Отдельная находка по ходу проверки, к этому PR не относится: у всех 27 строк OCR-пути act_date взята из первого попавшегося «от DD.MM.YYYY» в тексте, и 11 строк из двух разных документов (развязка на Сибирском тракте и улица Энергостроителей) делят одну дату 2004-07-06 при проектах 2019-х годов. Выношу отдельно.

Как проверено

  • Двусторонне: против origin/main два теста красные с конкретным неверным значением: assert 'резервирование' == 'изъятие'.
  • Контроль симметрии (test_rezervirovanie_in_title_still_wins) зелёный с обеих сторон — он бы покраснел на «починке» через смену порядка проверок.
  • Контроль размножения (test_all_parcels_of_the_document_are_affected) идёт через extract_reservations на трёх реальных кад-номерах с прода: показывает, что ошибка уходит во все строки, а не в одну.
  • Контроли одиночных маркеров и отката к default_kind — зелёные с обеих сторон.
  • Формулировки в тестах взяты с прода дословно (basis_act: «Сообщение о планируемом изъятии земельных участков…»).
  • pytest backend/tests/services/scrapers/ — 310 passed, 6 skipped. Существующие 14 тестов парсера не тронуты.

Часть эпика #2464.

## Дефект `_detect_kind` проверял «резервир» **первым** и возвращал `резервирование` безусловно: ```python if _RE_REZERV.search(text): return "резервирование" if _RE_IZYAT.search(text): return "изъятие" ``` Постановление об изъятии, где резервирование упомянуто вскользь — типовая формулировка «ранее зарезервированных земель», ссылка на утративший силу акт о резервировании — классифицируется как резервирование. **Ошибка не единичная.** `kind` в `extract_reservations` вычисляется один раз на весь документ и проставляется каждой записи: ```python kind = _detect_kind(text, default_kind) for m in _RE_CAD_NUM.finditer(text): records.append(ReservationRecord(cad_num=cad, reservation_kind=kind, ...)) ``` Один документ с 40 участками → 40 неверных строк в `land_reservation`. ## Правка Побеждает то слово, что встретилось **раньше**. Тема документа стоит в заголовке, поэтому позиция — сигнал сильнее порядка проверок. Важно, что решение **симметрично**: заголовок «О резервировании» так же выигрывает у «изъятия» в теле («зарезервированные участки не подлежат изъятию»). Простая смена порядка проверок дала бы обратный перекос — на это поставлен отдельный контроль. ## Текущих ошибок на проде нет — измерено Уточняю первую редакцию этого описания: 27 строк в `land_reservation` **не проходили через этот парсер**. Они из другой трубы. ``` land_reservation по источникам: izyatie_ekb_ocr 27 ← izyatie_ocr.py, OCR со страниц екатеринбург.рф page_pdf 0 ← page_reservation_parser (этот PR) pravo_gov66 0 ← page_reservation_parser (этот PR) ``` `izyatie_ocr.py` вообще не зовёт `_detect_kind` — он ставит `reservation_kind = "изъятие"` константой. То есть парсер из этого PR **не записал на прод ни одной строки**, и текущей порчи от дефекта нет по той же причине, по какой нет и данных. Правка закрывает возможность, а не чинит существующую порчу — говорю это прямо, чтобы никто не записал сюда прод-победу. Отдельная находка по ходу проверки, к этому PR не относится: у всех 27 строк OCR-пути `act_date` взята из первого попавшегося «от DD.MM.YYYY» в тексте, и 11 строк из **двух разных** документов (развязка на Сибирском тракте и улица Энергостроителей) делят одну дату 2004-07-06 при проектах 2019-х годов. Выношу отдельно. ## Как проверено - **Двусторонне:** против `origin/main` два теста красные с конкретным неверным значением: `assert 'резервирование' == 'изъятие'`. - **Контроль симметрии** (`test_rezervirovanie_in_title_still_wins`) зелёный с обеих сторон — он бы покраснел на «починке» через смену порядка проверок. - **Контроль размножения** (`test_all_parcels_of_the_document_are_affected`) идёт через `extract_reservations` на трёх реальных кад-номерах с прода: показывает, что ошибка уходит во все строки, а не в одну. - Контроли одиночных маркеров и отката к `default_kind` — зелёные с обеих сторон. - Формулировки в тестах взяты с прода дословно (`basis_act`: «Сообщение о планируемом изъятии земельных участков…»). - `pytest backend/tests/services/scrapers/` — 310 passed, 6 skipped. Существующие 14 тестов парсера не тронуты. Часть эпика #2464.
bot-backend added 1 commit 2026-08-20 12:49:47 +00:00
fix(ptica): тип постановления — по первому упоминанию, а не по порядку проверок (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 17m12s
71f27fee7d
`_detect_kind` проверял «резервир» первым и возвращал «резервирование»
безусловно. Постановление об изъятии, где резервирование упомянуто
вскользь — типовая формулировка «ранее зарезервированных земель», ссылка
на утративший силу акт, — классифицировалось как резервирование.

Ошибка не единичная: `kind` в `extract_reservations` один на весь
документ, поэтому неверный тип уходит в КАЖДУЮ строку land_reservation
по этому акту.

Побеждает то слово, что встретилось раньше. Тема документа стоит в
заголовке, поэтому позиция — сигнал сильнее порядка проверок, и он
симметричен: заголовок «О резервировании» так же выигрывает у «изъятия»
в теле. Простая смена порядка проверок этой симметрии не даёт — на неё
поставлен отдельный контроль.

Текущих ошибок на проде нет, и это измерено: в land_reservation 27
строк, все из источника izyatie_ekb_ocr, все «изъятие», ни в одной
выдержке слова «резервир» не встречается. Правка закрывает возможность,
а не чинит существующую порчу.

Двусторонне: против origin/main два теста красные с конкретным неверным
значением (`assert 'резервирование' == 'изъятие'`). Контроли —
симметрия заголовка, одиночные маркеры, откат к default_kind — зелёные с
обеих сторон. Формулировки в тестах взяты с прода дословно (basis_act).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit 46beeb4c13 into main 2026-08-20 16:28:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2980
No description provided.