feat: extract workflow material services phase 3
This commit is contained in:
@@ -176,80 +176,17 @@ def _auto_populate_materials_for_cad(cad_file_id: str, tenant_id: str | None = N
|
||||
Only fills products where cad_part_materials is empty or all-blank,
|
||||
preventing overwrites of manually assigned materials.
|
||||
"""
|
||||
from sqlalchemy import create_engine, select as sql_select, update as sql_update
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
from app.config import settings as app_settings
|
||||
from app.models.cad_file import CadFile
|
||||
from app.models.product import Product
|
||||
from app.api.routers.products import build_materials_from_excel
|
||||
from app.services.step_processor import build_part_colors
|
||||
from app.core.tenant_context import set_tenant_context_sync
|
||||
from app.domains.rendering.workflow_runtime_services import auto_populate_materials_for_cad
|
||||
|
||||
sync_url = app_settings.database_url.replace("+asyncpg", "")
|
||||
eng = create_engine(sync_url)
|
||||
with Session(eng) as session:
|
||||
set_tenant_context_sync(session, tenant_id)
|
||||
# Load the CAD file to get parsed objects
|
||||
cad_file = session.execute(
|
||||
sql_select(CadFile).where(CadFile.id == cad_file_id)
|
||||
).scalar_one_or_none()
|
||||
if cad_file is None:
|
||||
return
|
||||
|
||||
parsed_objects = cad_file.parsed_objects or {}
|
||||
cad_parts: list[str] = parsed_objects.get("objects", [])
|
||||
if not cad_parts:
|
||||
return
|
||||
|
||||
# Find products linked to this CAD file that have Excel components
|
||||
products = session.execute(
|
||||
sql_select(Product).where(
|
||||
Product.cad_file_id == cad_file.id,
|
||||
Product.is_active.is_(True),
|
||||
)
|
||||
).scalars().all()
|
||||
|
||||
final_part_colors = None
|
||||
for product in products:
|
||||
excel_components: list[dict] = product.components or []
|
||||
if not excel_components:
|
||||
continue
|
||||
|
||||
# Only auto-fill when cad_part_materials is empty or all-blank
|
||||
existing = product.cad_part_materials or []
|
||||
if existing and any(m.get("material", "").strip() for m in existing):
|
||||
continue # has at least one real material — don't overwrite
|
||||
|
||||
new_materials = build_materials_from_excel(cad_parts, excel_components)
|
||||
session.execute(
|
||||
sql_update(Product)
|
||||
.where(Product.id == product.id)
|
||||
.values(cad_part_materials=new_materials)
|
||||
)
|
||||
session.flush()
|
||||
|
||||
# Compute part colors; thumbnail queued once after the loop
|
||||
try:
|
||||
final_part_colors = build_part_colors(cad_parts, new_materials)
|
||||
except Exception:
|
||||
logger.exception(f"Part colors build failed for product {product.id}")
|
||||
|
||||
logger.info(
|
||||
f"Auto-populated {len(new_materials)} materials for product {product.id} "
|
||||
f"from {len(excel_components)} Excel components"
|
||||
)
|
||||
|
||||
session.commit()
|
||||
|
||||
# Queue exactly ONE thumbnail regeneration per CAD file regardless of how many
|
||||
# products were auto-populated. Queuing once-per-product multiplies the task
|
||||
# count needlessly and causes the Redis queue depth to grow instead of shrink.
|
||||
if final_part_colors is not None:
|
||||
try:
|
||||
from app.domains.pipeline.tasks.render_thumbnail import regenerate_thumbnail
|
||||
regenerate_thumbnail.delay(str(cad_file_id), final_part_colors)
|
||||
except Exception:
|
||||
logger.exception(f"Thumbnail regen queue failed for cad_file {cad_file_id}")
|
||||
auto_populate_materials_for_cad(session, cad_file_id)
|
||||
|
||||
eng.dispose()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user