Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
@@ -0,0 +1,129 @@
"""AgroStar табличные форматы (PDF, xlsx) → те же AgrostarSample, что и XML."""
from __future__ import annotations
import re
from typing import Any
from app.modules.zootech.lab.etl.agrostar_xml_import import (
AgrostarParseResult,
AgrostarSample,
_parse_float,
_pct_dm_to_g_per_kg_sv,
finalize_agrostar_sample,
)
# Подпись в отчёте → ключ AgroStar XML (дальше — общий _AGROSTAR_MAP)
_TABULAR_LABEL_TO_AGRO: dict[str, str] = {
"Сухое вещество (DM)": "DM",
"Сырой протеин (Crude Protein)": "CP",
"КДК (ADF)": "ADF",
"НДК (aNDF)": "NDF",
"НДК по орг. веществу (aNDFom)": "aNDFom",
"Сырой жир (Fat EE)": "Fat_EE",
"Сырая зола (Ash)": "Ash",
"Кальций (Ca)": "Ca",
"Фосфор (P)": "P",
"Магний (Mg)": "Mg",
"Калий (K)": "K",
"Сера (S)": "S",
"Хлор (Cl)": "Cl",
"Водорастворимый сахар (WSC)": "Sugar_WSC",
"Cпирторастворимый сахар (ESC)": "Sugar_ESC",
"Крахмал (Starch)": "Starch",
"Безволокнистые углеводы (NFC)": "NFC",
"Переваримые питательные вещества (TDN)": "TDN",
"НДК протеин (ND-ICP)": "NDICP_CP",
"Лизин": "Lys",
"Метионин": "Met",
"Лейцин": "Leu",
"Изолейцин": "Ile",
"Валин": "Val",
"Усваиваемость НДК 30 ч (NDFD30)": "NDFDom_IV_30hr",
}
_SECTION_HEADERS = frozenset(
{
"Протеин:",
"Клетчатка:",
"Сахара и крахмал:",
"Жир и жирные кислоты:",
"Минералы:",
"Энергия:",
}
)
def map_tabular_label(label: str) -> str | None:
text = (label or "").strip()
if not text or text in _SECTION_HEADERS:
return None
if text in _TABULAR_LABEL_TO_AGRO:
return _TABULAR_LABEL_TO_AGRO[text]
lowered = text.lower()
for key, ag_key in sorted(_TABULAR_LABEL_TO_AGRO.items(), key=lambda item: len(item[0]), reverse=True):
if key.lower() in lowered:
return ag_key
return None
def tabular_rows_to_agro_fields(rows: list[tuple[str, str]]) -> dict[str, float]:
"""[(label, raw_value), ...] → raw_fields AgroStar."""
out: dict[str, float] = {}
for label, raw in rows:
ag_key = map_tabular_label(label)
if not ag_key:
continue
val = _parse_float(raw)
if val is not None:
out[ag_key] = val
return out
def sample_from_agro_fields(
*,
sample_no: str,
farm_name: str,
farm_id: str = "",
date_printed: str = "",
feed_type: str = "Mixed haylage",
desc_1: str = "",
desc_2: str = "",
desc_3: str = "",
agro_fields: dict[str, float],
) -> AgrostarSample:
sample = AgrostarSample(
sample_no=sample_no,
farm_name=farm_name,
farm_id=farm_id,
date_printed=date_printed,
feed_type=feed_type,
desc_1=desc_1,
desc_2=desc_2,
desc_3=desc_3,
dry_matter_pct=agro_fields.get("DM"),
)
sample.raw_fields = {k: v for k, v in agro_fields.items() if k != "DM"}
return finalize_agrostar_sample(sample)
def parse_tabular_triplet_lines(lines: list[str]) -> list[tuple[str, str, str]]:
"""Строки PDF: name, unit (%DM|%CP|%), value."""
rows: list[tuple[str, str, str]] = []
i = 0
units = frozenset({"%DM", "%CP", "%"})
while i < len(lines):
line = lines[i]
if line in units and i >= 1:
name = lines[i - 1]
val = lines[i + 1] if i + 1 < len(lines) else ""
if val and (val[0].isdigit() or val.startswith("<")):
rows.append((name, line, val))
i += 2
continue
i += 1
return rows
def rows_to_label_values(triplets: list[tuple[str, str, str]]) -> list[tuple[str, str]]:
return [(name, val) for name, _unit, val in triplets]