@@ -0,0 +1,138 @@
|
||||
"""lab_component_nutrients: zootech-показатели в колонках (не JSON на component).
|
||||
|
||||
Revision ID: lab_component_nutrients
|
||||
Revises: lab_module
|
||||
Create Date: 2026-06-08
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision = "lab_component_nutrients"
|
||||
down_revision = "lab_module"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _normalize_key(value: str) -> str:
|
||||
return " ".join((value or "").split()).strip().lower()
|
||||
|
||||
|
||||
def _read_num(data: dict, keys: tuple[str, ...]) -> float | None:
|
||||
for search in keys:
|
||||
target = _normalize_key(search)
|
||||
for k, v in data.items():
|
||||
nk = _normalize_key(str(k))
|
||||
if nk == target or target in nk or nk in target:
|
||||
try:
|
||||
n = float(v)
|
||||
if n == n:
|
||||
return n
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
_FIELD_KEYS = {
|
||||
"crude_protein": ("Сыр. Протеин",),
|
||||
"usp": ("уСП",),
|
||||
"bra": ("БРА", " БРА "),
|
||||
"nel_cattle": ("ЧЭЛ- КРС", " ЧЭЛ- КРС"),
|
||||
"oe_cattle": ("ОЭ-КРС", " ОЭ-КРС"),
|
||||
"ndf": ("Сырая клетчатка", "Сырая клетч"),
|
||||
"structural_fiber": ("Структур. клетчатка", "Структур клетч"),
|
||||
"crude_fat": ("Сырой жир",),
|
||||
}
|
||||
|
||||
|
||||
def _migrate_json_nutrients(bind) -> None:
|
||||
insp = sa.inspect(bind)
|
||||
if "component" not in insp.get_table_names():
|
||||
return
|
||||
cols = {c["name"] for c in insp.get_columns("component")}
|
||||
if "nutrients" not in cols:
|
||||
return
|
||||
rows = bind.execute(sa.text("SELECT id, nutrients FROM component WHERE is_deleted = 0")).fetchall()
|
||||
for comp_id, raw in rows:
|
||||
try:
|
||||
data = json.loads(raw or "{}")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
data = {}
|
||||
if not isinstance(data, dict) or not data:
|
||||
continue
|
||||
payload = {col: _read_num(data, keys) for col, keys in _FIELD_KEYS.items()}
|
||||
if not any(v is not None for v in payload.values()):
|
||||
continue
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
INSERT INTO lab_component_nutrients (
|
||||
component_id, crude_protein, usp, bra, nel_cattle,
|
||||
oe_cattle, ndf, structural_fiber, crude_fat
|
||||
) VALUES (
|
||||
:component_id, :crude_protein, :usp, :bra, :nel_cattle,
|
||||
:oe_cattle, :ndf, :structural_fiber, :crude_fat
|
||||
)
|
||||
ON CONFLICT(component_id) DO UPDATE SET
|
||||
crude_protein = excluded.crude_protein,
|
||||
usp = excluded.usp,
|
||||
bra = excluded.bra,
|
||||
nel_cattle = excluded.nel_cattle,
|
||||
oe_cattle = excluded.oe_cattle,
|
||||
ndf = excluded.ndf,
|
||||
structural_fiber = excluded.structural_fiber,
|
||||
crude_fat = excluded.crude_fat
|
||||
"""
|
||||
),
|
||||
{"component_id": comp_id, **payload},
|
||||
)
|
||||
|
||||
|
||||
def upgrade(**kw) -> None:
|
||||
if kw.get("tag") == "reports":
|
||||
return
|
||||
bind = op.get_bind()
|
||||
insp = sa.inspect(bind)
|
||||
if "lab_component_nutrients" not in insp.get_table_names():
|
||||
op.create_table(
|
||||
"lab_component_nutrients",
|
||||
sa.Column("component_id", sa.String(length=36), primary_key=True),
|
||||
sa.Column("crude_protein", sa.Float(), nullable=True),
|
||||
sa.Column("usp", sa.Float(), nullable=True),
|
||||
sa.Column("bra", sa.Float(), nullable=True),
|
||||
sa.Column("nel_cattle", sa.Float(), nullable=True),
|
||||
sa.Column("oe_cattle", sa.Float(), nullable=True),
|
||||
sa.Column("ndf", sa.Float(), nullable=True),
|
||||
sa.Column("structural_fiber", sa.Float(), nullable=True),
|
||||
sa.Column("crude_fat", sa.Float(), nullable=True),
|
||||
)
|
||||
_migrate_json_nutrients(bind)
|
||||
|
||||
if "component" in insp.get_table_names():
|
||||
cols = {c["name"] for c in insp.get_columns("component")}
|
||||
if "nutrients" in cols:
|
||||
with op.batch_alter_table("component") as batch:
|
||||
batch.drop_column("nutrients")
|
||||
|
||||
|
||||
def downgrade(**kw) -> None:
|
||||
if kw.get("tag") == "reports":
|
||||
return
|
||||
bind = op.get_bind()
|
||||
insp = sa.inspect(bind)
|
||||
|
||||
if "component" in insp.get_table_names():
|
||||
cols = {c["name"] for c in insp.get_columns("component")}
|
||||
if "nutrients" not in cols:
|
||||
with op.batch_alter_table("component") as batch:
|
||||
batch.add_column(
|
||||
sa.Column("nutrients", sa.Text(), nullable=False, server_default="{}")
|
||||
)
|
||||
|
||||
if "lab_component_nutrients" in insp.get_table_names():
|
||||
op.drop_table("lab_component_nutrients")
|
||||
Reference in New Issue
Block a user