All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
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 2m0s
CI / backend-tests (pull_request) Successful in 17m13s
`_extract_act_date` брал ПЕРВОЕ «от DD.MM.YYYY» во всём OCR-тексте.
«Сообщение о планируемом изъятии» открывается списком оснований, и первой
строкой там стоит
«Решение Екатеринбургской городской Думы от 06.07.2004 № 60/1
«Об утверждении Генерального плана города»»
— Генплан, а не акт об изъятии. На проде это дало 11 строк из 27 с датой
2004-07-06 при проектах 2020 и 2022 годов, причём одну и ту же дату
получили ДВА разных документа (развязка на Сибирском тракте и улица
Энергостроителей). Совпадение даты у несвязанных актов и было первым
признаком, что дата не своя.
Дата принимается, только если в 120 символах перед ней стоит слово
«постановлени». Ссылки-помехи в этих документах — «Решение … Думы» и
«Приказ Министерства» — его не содержат. Окно шире самой фразы, потому
что OCR перемешивает колонки таблицы и вклинивает в неё чужой текст
(«…Администрации города документами) Екатеринбурга от 19.04.2019…»).
Если подходящей даты нет — None. Дата чужого документа хуже пустоты: по
ней нельзя ни отфильтровать актуальные изъятия, ни сверить срок, и она
неотличима от настоящей.
Калибровка не на одном образце: все пять исходных PDF загружены и
распознаны тем же трактом, что использует загрузчик (ocr_pdf_text в
прод-контейнере). Окна 80/120/160 дают одинаковые 5 из 5. Более узкое
правило (плюс «администраци») давало те же 5 из 5, но ломало законный
случай «Постановление № 509-ПП» — областной акт без слова «администрация»,
уже закреплённый тестом test_act_date_extracted_from_text; взято широкое.
Сквозная проверка: патченный код прогнан по всем пяти распознанным
текстам целиком — 27 записей, ровно столько же, сколько строк в
land_reservation; 11 меняют 2004-07-06 на настоящую дату, 16 не двигаются.
Двусторонне: против origin/main три теста красные с реальным неверным
значением ('2004-07-06'), ни одного TypeError — тесты идут через
extract_izyatie_records, чья сигнатура одинакова на обеих сторонах.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
149 lines
10 KiB
Python
149 lines
10 KiB
Python
"""act_date — дата акта об изъятии, а не первой попавшейся ссылки (#2464).
|
||
|
||
`_extract_act_date` брал ПЕРВОЕ «от DD.MM.YYYY» во всём OCR-тексте. «Сообщение о
|
||
планируемом изъятии» открывается списком оснований, и первой строкой там почти
|
||
всегда стоит
|
||
|
||
«Решение Екатеринбургской городской Думы от 06.07.2004 № 60/1
|
||
«Об утверждении Генерального плана города»»
|
||
|
||
— то есть Генплан, а не акт об изъятии. На проде это дало 11 строк из 27
|
||
(два РАЗНЫХ документа: развязка на Сибирском тракте и улица Энергостроителей)
|
||
с одной датой 2004-07-06 при проектах 2020 и 2022 годов.
|
||
|
||
Проверено не по догадке: все пять исходных PDF были загружены и распознаны тем
|
||
же трактом, что использует загрузчик (`ocr_pdf_text` в прод-контейнере). Тексты
|
||
цитат ниже — дословно оттуда, включая перемешивание колонок OCR'ом.
|
||
|
||
Правило («постановлени» в 120 символах перед датой) откалибровано на всех пяти
|
||
документах сразу, а не на одном: окна 80/120/160 дают одинаковые 5 из 5.
|
||
Более узкий вариант (плюс «администраци») давал те же 5 из 5, но ломал законный
|
||
случай «Постановление № 509-ПП» — областной акт без слова «администрация»,
|
||
уже закреплённый тестом test_act_date_extracted_from_text. Взято широкое условие.
|
||
|
||
Патченный код прогнан по всем пяти распознанным текстам целиком: 27 записей —
|
||
ровно столько же, сколько строк в land_reservation, из них 11 меняют дату
|
||
2004-07-06 на настоящую, 16 остаются прежними.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from app.services.scrapers.izyatie_ocr import _extract_act_date, extract_izyatie_records
|
||
|
||
# Заголовок раздела — дословно с прода (basis_act в land_reservation).
|
||
ЗАГОЛОВОК = (
|
||
"Сообщение о планируемом изъятии земельных участков и объектов недвижимого "
|
||
"имущества для муниципальных нужд"
|
||
)
|
||
КАД = "66:41:0303004:22"
|
||
|
||
|
||
def _дата_записи(текст_документа: str) -> str | None:
|
||
"""act_date так, как её увидит land_reservation — через сквозной путь.
|
||
|
||
Зовём `extract_izyatie_records`, а не приватный хелпер: его сигнатура
|
||
одинакова на обеих сторонах, поэтому против origin/main тест краснеет
|
||
НЕВЕРНЫМ ЗНАЧЕНИЕМ (дата Генплана), а не TypeError из-за нового параметра.
|
||
"""
|
||
записи = extract_izyatie_records(f"{текст_документа}\n{КАД}", ЗАГОЛОВОК, "http://x/y")
|
||
assert записи, "кад-номер не разобран — тест смотрел бы не туда"
|
||
return записи[0]["act_date"]
|
||
|
||
|
||
# ── Дословные фрагменты OCR прод-документов ───────────────────────────────────
|
||
|
||
# 89adb28a… — развязка Базовый/Комсомольская/Сибирский тракт. На проде дал 2004-07-06.
|
||
ДОК_РАЗВЯЗКА = (
|
||
"Администрация города Екатеринбурга\n\n"
|
||
"— Решение Екатеринбургской городской Думы от\n06.07.2004 № 60/1 «Об утверждении\n"
|
||
"Генерального плана развития городского округа»;\n\n"
|
||
"— Приказ Министерства строительства и развития\nинфраструктуры Свердловской области от\n"
|
||
"30.12.2021 № 746-П;\n\n"
|
||
"— Постановление Администрации города\nЕкатеринбурга от 27.05.2022 № 1413\n"
|
||
"«Об утверждении проекта планировки и проекта межевания территории»"
|
||
)
|
||
|
||
# 9b9d9a99… — улица Энергостроителей. На проде тоже дал 2004-07-06.
|
||
ДОК_ЭНЕРГОСТРОИТЕЛЕЙ = (
|
||
"Администрация города Екатеринбурга\n\n"
|
||
"— Решение Екатеринбургской городской Думы от\n06.07.2004 № 60/1 «Об утверждении\n"
|
||
"Генерального плана развития городского округа»;\n\n"
|
||
"— Приказ Министерства строительства и развития\nинфраструктуры Свердловской области от\n"
|
||
"30.12.2021 № 746-П «О внесении изменений в Генеральный план на период до 2025 года»;\n\n"
|
||
"— Постановление Администрации города\nЕкатеринбурга от 12.02.2020 № 259 «Об утверждении\n"
|
||
"проекта планировки и проекта межевания территории»"
|
||
)
|
||
|
||
# bf2bb1bd… — OCR перемешал колонки таблицы: слова фразы идут вперемешку.
|
||
ДОК_ПЕРЕМЕШАННЫЙ = (
|
||
"Постановление Администрации города утверждении проекта планировки\n"
|
||
"Екатеринбурга от 29.09.2015 № 2687 территории и проекта межевания\n"
|
||
"«Об утверждении» (в ред. Постановления Администрации города\n"
|
||
"Екатеринбурга от 14.07.2022 № 1995)"
|
||
)
|
||
|
||
# ccd00215… — то же перемешивание, но короче.
|
||
ДОК_ПЕРЕМЕШАННЫЙ_2 = (
|
||
"Реквизиты документов (при наличии таких Постановление Администрации города\n"
|
||
"документами) Екатеринбурга от 19.04.2019 № 863 «Об\nутверждении проекта межевания территории»"
|
||
)
|
||
|
||
# 188a5c36… — первой же датой идёт нужное постановление.
|
||
ДОК_ПРЯМОЙ = (
|
||
"Реквизиты документов (при наличии таких документами)\n"
|
||
"Постановление Администрации города Екатеринбурга от 24.06.2019 № 1504\n"
|
||
"«Об утверждении проекта планировки и проекта межевания территории»"
|
||
)
|
||
|
||
|
||
def test_genplan_citation_is_not_taken_as_act_date() -> None:
|
||
"""Головной: дата Генплана-2004 не должна становиться датой изъятия.
|
||
|
||
На origin/main возвращается '2004-07-06' — дата чужого документа,
|
||
на 18 лет раньше настоящего основания.
|
||
"""
|
||
got = _дата_записи(ДОК_РАЗВЯЗКА)
|
||
assert got != "2004-07-06", "взята дата Решения Думы об утверждении Генерального плана"
|
||
assert got == "2022-05-27", f"ожидали дату постановления № 1413, получили {got!r}"
|
||
|
||
|
||
def test_second_document_with_same_genplan_citation() -> None:
|
||
"""Тот же дефект во втором документе — он и дал одинаковую дату у 11 строк."""
|
||
got = _дата_записи(ДОК_ЭНЕРГОСТРОИТЕЛЕЙ)
|
||
assert got == "2020-02-12", f"ожидали дату постановления № 259, получили {got!r}"
|
||
|
||
|
||
def test_ministry_order_is_not_taken_either() -> None:
|
||
"""Контроль: приказ Министерства между Генпланом и постановлением тоже не берётся."""
|
||
got = _дата_записи(ДОК_РАЗВЯЗКА)
|
||
assert got != "2021-12-30", "взята дата приказа Министерства строительства"
|
||
|
||
|
||
def test_ocr_column_scramble_still_resolves() -> None:
|
||
"""Контроль ширины окна: перемешанные OCR'ом колонки не должны ломать разбор.
|
||
|
||
Окно шире самой фразы (~50 символов) именно ради этих двух документов:
|
||
между «Постановление» и «от DD.MM.YYYY» вклинивается текст соседней колонки.
|
||
"""
|
||
assert _дата_записи(ДОК_ПЕРЕМЕШАННЫЙ) == "2015-09-29"
|
||
assert _дата_записи(ДОК_ПЕРЕМЕШАННЫЙ_2) == "2019-04-19"
|
||
|
||
|
||
def test_correct_documents_unchanged() -> None:
|
||
"""Контроль от переусердствования: там, где на проде было верно, ничего не меняется."""
|
||
assert _дата_записи(ДОК_ПРЯМОЙ) == "2019-06-24"
|
||
|
||
|
||
def test_no_act_citation_gives_none_not_a_wrong_date() -> None:
|
||
"""Контроль: без основания — None, а не дата постороннего документа.
|
||
|
||
Отсутствие даты честнее: по чужой дате нельзя ни отфильтровать актуальные
|
||
изъятия, ни сверить срок, и она неотличима от настоящей.
|
||
"""
|
||
текст = "Решение Екатеринбургской городской Думы от 06.07.2004 № 60/1 «Об утверждении»"
|
||
assert _дата_записи(текст) is None
|
||
|
||
|
||
def test_title_path_does_not_require_context() -> None:
|
||
"""Контроль: у заголовка ссылок на чужие акты нет, там правило не применяется."""
|
||
assert _extract_act_date("Постановление от 27.05.2022 № 1413") == "2022-05-27"
|