@@ -0,0 +1,88 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user