89 lines
2.8 KiB
Python
89 lines
2.8 KiB
Python
"""AgroStar PDF (печатная форма анализа) → AgrostarParseResult."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
from app.lab.etl.agrostar_tabular import (
|
|
parse_tabular_triplet_lines,
|
|
rows_to_label_values,
|
|
sample_from_agro_fields,
|
|
tabular_rows_to_agro_fields,
|
|
)
|
|
from app.lab.etl.agrostar_xml_import import AgrostarParseResult
|
|
|
|
|
|
def _pdf_text(data: bytes) -> str:
|
|
try:
|
|
import fitz # pymupdf
|
|
except ImportError as exc:
|
|
raise RuntimeError("Для PDF нужен pymupdf (pip install pymupdf)") from exc
|
|
doc = fitz.open(stream=data, filetype="pdf")
|
|
parts = [doc[i].get_text() for i in range(doc.page_count)]
|
|
doc.close()
|
|
return "\n".join(parts)
|
|
|
|
|
|
def _line_after(lines: list[str], marker: str) -> str:
|
|
for i, line in enumerate(lines):
|
|
if line == marker and i + 1 < len(lines):
|
|
return lines[i + 1]
|
|
return ""
|
|
|
|
|
|
def _is_agrostar_pdf(text: str) -> bool:
|
|
t = text or ""
|
|
return "ИНФОРМАЦИЯ ОБ ОБРАЗЦЕ" in t or "РЕЗУЛЬТАТЫ АНАЛИЗА" in t
|
|
|
|
|
|
def parse_agrostar_pdf(data: bytes) -> AgrostarParseResult:
|
|
result = AgrostarParseResult(lab_name="АгроСтар")
|
|
try:
|
|
text = _pdf_text(data)
|
|
except RuntimeError as exc:
|
|
result.errors.append(str(exc))
|
|
return result
|
|
|
|
if not _is_agrostar_pdf(text):
|
|
result.errors.append("Не похоже на отчёт AgroStar (PDF)")
|
|
return result
|
|
|
|
lines = [ln.strip() for ln in text.splitlines() if ln.strip()]
|
|
sample_no = _line_after(lines, "Образец №")
|
|
farm_name = _line_after(lines, "Клиент")
|
|
date_printed = _line_after(lines, "Дата анализа")
|
|
desc = _line_after(lines, "Описание")
|
|
|
|
label_parts: list[str] = []
|
|
if "Образец" in lines:
|
|
idx = lines.index("Образец")
|
|
if idx + 1 < len(lines) and lines[idx + 1] != "Описание":
|
|
label_parts.append(lines[idx + 1])
|
|
if idx + 2 < len(lines) and lines[idx + 2] not in ("Описание", "Клиент"):
|
|
label_parts.append(lines[idx + 2])
|
|
|
|
triplets = parse_tabular_triplet_lines(lines)
|
|
agro_fields = tabular_rows_to_agro_fields(rows_to_label_values(triplets))
|
|
if not agro_fields:
|
|
result.errors.append("В PDF не найдены показатели анализа")
|
|
return result
|
|
|
|
sample = sample_from_agro_fields(
|
|
sample_no=sample_no,
|
|
farm_name=farm_name,
|
|
date_printed=date_printed,
|
|
desc_1=desc or " ".join(label_parts),
|
|
agro_fields=agro_fields,
|
|
)
|
|
result.samples.append(sample)
|
|
return result
|
|
|
|
|
|
def looks_like_agrostar_pdf(data: bytes) -> bool:
|
|
try:
|
|
text = _pdf_text(data)
|
|
except RuntimeError:
|
|
return False
|
|
return _is_agrostar_pdf(text)
|