Fix forecasting clone endpoint for demo sessions

Fixed two critical issues preventing forecast data from being cloned:

1. **Missing batch_name field**: The fixture uses `batch_id` but the
   PredictionBatch model requires `batch_name` (NOT NULL constraint).
   Added field mapping to handle batch_id -> batch_name conversion.

2. **UUID type mismatch**: The fixture's `product_id` is a string but
   the Forecast model expects `inventory_product_id` as UUID type.
   Added conversion from string to UUID.

3. **Field mappings added**:
   - batch_id -> batch_name
   - total_forecasts -> total_products
   - created_at -> requested_at (fallback)
   - Calculated completed_products from status

These fixes enable the forecasting service to successfully clone all
28 forecasts from the fixture file, unlocking demand forecasting
AI insights in demo sessions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Urtzi Alfaro
2025-12-16 07:36:03 +01:00
parent c566967bea
commit 35ae23b381

View File

@@ -244,7 +244,12 @@ async def clone_demo_data(
# Create forecast # Create forecast
# Map product_id to inventory_product_id if needed # Map product_id to inventory_product_id if needed
inventory_product_id = forecast_data.get('inventory_product_id') or forecast_data.get('product_id') inventory_product_id_str = forecast_data.get('inventory_product_id') or forecast_data.get('product_id')
# Convert to UUID if it's a string
if isinstance(inventory_product_id_str, str):
inventory_product_id = uuid.UUID(inventory_product_id_str)
else:
inventory_product_id = inventory_product_id_str
# Map predicted_quantity to predicted_demand if needed # Map predicted_quantity to predicted_demand if needed
predicted_demand = forecast_data.get('predicted_demand') or forecast_data.get('predicted_quantity') predicted_demand = forecast_data.get('predicted_demand') or forecast_data.get('predicted_quantity')
@@ -317,43 +322,31 @@ async def clone_demo_data(
detail=f"Invalid UUID format in batch data: {str(e)}" detail=f"Invalid UUID format in batch data: {str(e)}"
) )
# Transform dates using proper parse_date_field function
for date_field in ['requested_at', 'completed_at']:
if date_field in batch_data:
try:
parsed_date = parse_date_field(
batch_data[date_field],
session_time,
date_field
)
if parsed_date:
batch_data[date_field] = parsed_date
else:
# If parsing fails, use session_time as fallback
batch_data[date_field] = session_time
logger.warning("Using fallback date for failed parsing",
date_field=date_field,
original_value=batch_data[date_field])
except Exception as e:
logger.warning("Failed to parse date, using fallback",
date_field=date_field,
date_value=batch_data[date_field],
error=str(e))
batch_data[date_field] = session_time
# Create prediction batch # Create prediction batch
# Handle field mapping: batch_id -> batch_name, total_forecasts -> total_products
batch_name = batch_data.get('batch_name') or batch_data.get('batch_id') or f"Batch-{transformed_id}"
total_products = batch_data.get('total_products') or batch_data.get('total_forecasts') or 0
completed_products = batch_data.get('completed_products') or (total_products if batch_data.get('status') == 'COMPLETED' else 0)
# Parse dates (handle created_at or prediction_date for requested_at)
requested_at_raw = batch_data.get('requested_at') or batch_data.get('created_at') or batch_data.get('prediction_date')
requested_at = parse_date_field(requested_at_raw, session_time, 'requested_at') if requested_at_raw else session_time
completed_at_raw = batch_data.get('completed_at')
completed_at = parse_date_field(completed_at_raw, session_time, 'completed_at') if completed_at_raw else None
new_batch = PredictionBatch( new_batch = PredictionBatch(
id=transformed_id, id=transformed_id,
tenant_id=virtual_uuid, tenant_id=virtual_uuid,
batch_name=batch_data.get('batch_name'), batch_name=batch_name,
requested_at=batch_data.get('requested_at'), requested_at=requested_at,
completed_at=batch_data.get('completed_at'), completed_at=completed_at,
status=batch_data.get('status'), status=batch_data.get('status', 'completed'),
total_products=batch_data.get('total_products'), total_products=total_products,
completed_products=batch_data.get('completed_products'), completed_products=completed_products,
failed_products=batch_data.get('failed_products'), failed_products=batch_data.get('failed_products', 0),
forecast_days=batch_data.get('forecast_days'), forecast_days=batch_data.get('forecast_days', 7),
business_type=batch_data.get('business_type'), business_type=batch_data.get('business_type', 'individual'),
error_message=batch_data.get('error_message'), error_message=batch_data.get('error_message'),
processing_time_ms=batch_data.get('processing_time_ms'), processing_time_ms=batch_data.get('processing_time_ms'),
cancelled_by=batch_data.get('cancelled_by') cancelled_by=batch_data.get('cancelled_by')