"""lab_component_nutrient_value: полная матрица показателей (EAV). Revision ID: lab_component_nutrient_value Revises: drop_lab_calculation_run Create Date: 2026-06-08 """ from __future__ import annotations import uuid import sqlalchemy as sa from alembic import op from app.lab.models import LabComponentNutrientValue revision = "lab_component_nutrient_value" down_revision = "drop_lab_calculation_run" branch_labels = None depends_on = None # 8 колонок lab_component_nutrients → заголовки FUTTERPLUS _SUMMARY_TO_HEADER = { "crude_protein": "Сыр. Протеин", "usp": "уСП", "bra": "БРА", "nel_cattle": "ЧЭЛ- КРС", "oe_cattle": "ОЭ-КРС", "ndf": "Сырая клетч", "structural_fiber": "Структур клетч", "crude_fat": "Сырой жир", } def _migrate_summary_to_eav(bind) -> None: insp = sa.inspect(bind) if "lab_component_nutrients" not in insp.get_table_names(): return rows = bind.execute( sa.text( """ SELECT component_id, crude_protein, usp, bra, nel_cattle, oe_cattle, ndf, structural_fiber, crude_fat FROM lab_component_nutrients """ ) ).fetchall() col_order = list(_SUMMARY_TO_HEADER.keys()) for row in rows: comp_id = row[0] for i, header in enumerate(_SUMMARY_TO_HEADER.values()): val = row[i + 1] if val is None: continue bind.execute( sa.text( """ INSERT INTO lab_component_nutrient_value (id, component_id, nutrient_key, value) VALUES (:id, :component_id, :nutrient_key, :value) ON CONFLICT(component_id, nutrient_key) DO UPDATE SET value = excluded.value """ ), { "id": str(uuid.uuid4()), "component_id": comp_id, "nutrient_key": header, "value": val, }, ) def upgrade(**kw) -> None: if kw.get("tag") == "reports": return bind = op.get_bind() LabComponentNutrientValue.__table__.create(bind, checkfirst=True) _migrate_summary_to_eav(bind) def downgrade(**kw) -> None: if kw.get("tag") == "reports": return bind = op.get_bind() if "lab_component_nutrient_value" in sa.inspect(bind).get_table_names(): op.drop_table("lab_component_nutrient_value")