Fix new services implementation 3
This commit is contained in:
@@ -29,7 +29,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
# Validate model data
|
||||
validation_result = self._validate_training_data(
|
||||
model_data,
|
||||
["tenant_id", "product_name", "model_path", "job_id"]
|
||||
["tenant_id", "inventory_product_id", "model_path", "job_id"]
|
||||
)
|
||||
|
||||
if not validation_result["is_valid"]:
|
||||
@@ -38,7 +38,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
# Check for duplicate active models for same tenant+product
|
||||
existing_model = await self.get_active_model_for_product(
|
||||
model_data["tenant_id"],
|
||||
model_data["product_name"]
|
||||
model_data["inventory_product_id"]
|
||||
)
|
||||
|
||||
# If there's an existing active model, we may want to deactivate it
|
||||
@@ -46,7 +46,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
logger.info("Deactivating previous production model",
|
||||
previous_model_id=existing_model.id,
|
||||
tenant_id=model_data["tenant_id"],
|
||||
product_name=model_data["product_name"])
|
||||
inventory_product_id=model_data["inventory_product_id"])
|
||||
await self.update(existing_model.id, {"is_production": False})
|
||||
|
||||
# Create new model
|
||||
@@ -55,7 +55,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
logger.info("Trained model created successfully",
|
||||
model_id=model.id,
|
||||
tenant_id=model.tenant_id,
|
||||
product_name=model.product_name,
|
||||
inventory_product_id=str(model.inventory_product_id),
|
||||
model_type=model.model_type)
|
||||
|
||||
return model
|
||||
@@ -65,21 +65,21 @@ class ModelRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to create trained model",
|
||||
tenant_id=model_data.get("tenant_id"),
|
||||
product_name=model_data.get("product_name"),
|
||||
inventory_product_id=model_data.get("inventory_product_id"),
|
||||
error=str(e))
|
||||
raise DatabaseError(f"Failed to create model: {str(e)}")
|
||||
|
||||
async def get_model_by_tenant_and_product(
|
||||
self,
|
||||
tenant_id: str,
|
||||
product_name: str
|
||||
inventory_product_id: str
|
||||
) -> List[TrainedModel]:
|
||||
"""Get all models for a tenant and product"""
|
||||
try:
|
||||
return await self.get_multi(
|
||||
filters={
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name
|
||||
"inventory_product_id": inventory_product_id
|
||||
},
|
||||
order_by="created_at",
|
||||
order_desc=True
|
||||
@@ -87,21 +87,21 @@ class ModelRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to get models by tenant and product",
|
||||
tenant_id=tenant_id,
|
||||
product_name=product_name,
|
||||
inventory_product_id=inventory_product_id,
|
||||
error=str(e))
|
||||
raise DatabaseError(f"Failed to get models: {str(e)}")
|
||||
|
||||
async def get_active_model_for_product(
|
||||
self,
|
||||
tenant_id: str,
|
||||
product_name: str
|
||||
inventory_product_id: str
|
||||
) -> Optional[TrainedModel]:
|
||||
"""Get the active production model for a product"""
|
||||
try:
|
||||
models = await self.get_multi(
|
||||
filters={
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name,
|
||||
"inventory_product_id": inventory_product_id,
|
||||
"is_active": True,
|
||||
"is_production": True
|
||||
},
|
||||
@@ -113,7 +113,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to get active model for product",
|
||||
tenant_id=tenant_id,
|
||||
product_name=product_name,
|
||||
inventory_product_id=inventory_product_id,
|
||||
error=str(e))
|
||||
raise DatabaseError(f"Failed to get active model: {str(e)}")
|
||||
|
||||
@@ -137,7 +137,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
# Deactivate other production models for the same tenant+product
|
||||
await self._deactivate_other_production_models(
|
||||
model.tenant_id,
|
||||
model.product_name,
|
||||
str(model.inventory_product_id),
|
||||
model_id
|
||||
)
|
||||
|
||||
@@ -150,7 +150,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
logger.info("Model promoted to production",
|
||||
model_id=model_id,
|
||||
tenant_id=model.tenant_id,
|
||||
product_name=model.product_name)
|
||||
inventory_product_id=str(model.inventory_product_id))
|
||||
|
||||
return updated_model
|
||||
|
||||
@@ -223,16 +223,16 @@ class ModelRepository(TrainingBaseRepository):
|
||||
|
||||
# Get models by product using raw query
|
||||
product_query = text("""
|
||||
SELECT product_name, COUNT(*) as count
|
||||
SELECT inventory_product_id, COUNT(*) as count
|
||||
FROM trained_models
|
||||
WHERE tenant_id = :tenant_id
|
||||
AND is_active = true
|
||||
GROUP BY product_name
|
||||
GROUP BY inventory_product_id
|
||||
ORDER BY count DESC
|
||||
""")
|
||||
|
||||
result = await self.session.execute(product_query, {"tenant_id": tenant_id})
|
||||
product_stats = {row.product_name: row.count for row in result.fetchall()}
|
||||
product_stats = {row.inventory_product_id: row.count for row in result.fetchall()}
|
||||
|
||||
# Recent activity (models created in last 30 days)
|
||||
thirty_days_ago = datetime.utcnow() - timedelta(days=30)
|
||||
@@ -274,7 +274,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
async def _deactivate_other_production_models(
|
||||
self,
|
||||
tenant_id: str,
|
||||
product_name: str,
|
||||
inventory_product_id: str,
|
||||
exclude_model_id: str
|
||||
) -> int:
|
||||
"""Deactivate other production models for the same tenant+product"""
|
||||
@@ -283,14 +283,14 @@ class ModelRepository(TrainingBaseRepository):
|
||||
UPDATE trained_models
|
||||
SET is_production = false
|
||||
WHERE tenant_id = :tenant_id
|
||||
AND product_name = :product_name
|
||||
AND inventory_product_id = :inventory_product_id
|
||||
AND id != :exclude_model_id
|
||||
AND is_production = true
|
||||
""")
|
||||
|
||||
result = await self.session.execute(query, {
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name,
|
||||
"inventory_product_id": inventory_product_id,
|
||||
"exclude_model_id": exclude_model_id
|
||||
})
|
||||
|
||||
@@ -299,7 +299,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to deactivate other production models",
|
||||
tenant_id=tenant_id,
|
||||
product_name=product_name,
|
||||
inventory_product_id=inventory_product_id,
|
||||
error=str(e))
|
||||
raise DatabaseError(f"Failed to deactivate models: {str(e)}")
|
||||
|
||||
@@ -313,7 +313,7 @@ class ModelRepository(TrainingBaseRepository):
|
||||
return {
|
||||
"model_id": model.id,
|
||||
"tenant_id": model.tenant_id,
|
||||
"product_name": model.product_name,
|
||||
"inventory_product_id": str(model.inventory_product_id),
|
||||
"model_type": model.model_type,
|
||||
"metrics": {
|
||||
"mape": model.mape,
|
||||
|
||||
@@ -29,7 +29,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
# Validate metric data
|
||||
validation_result = self._validate_training_data(
|
||||
metric_data,
|
||||
["model_id", "tenant_id", "product_name"]
|
||||
["model_id", "tenant_id", "inventory_product_id"]
|
||||
)
|
||||
|
||||
if not validation_result["is_valid"]:
|
||||
@@ -45,7 +45,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
logger.info("Performance metric created",
|
||||
model_id=metric.model_id,
|
||||
tenant_id=metric.tenant_id,
|
||||
product_name=metric.product_name)
|
||||
inventory_product_id=str(metric.inventory_product_id))
|
||||
|
||||
return metric
|
||||
|
||||
@@ -97,7 +97,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
async def get_metrics_by_tenant_and_product(
|
||||
self,
|
||||
tenant_id: str,
|
||||
product_name: str,
|
||||
inventory_product_id: str,
|
||||
skip: int = 0,
|
||||
limit: int = 100
|
||||
) -> List[ModelPerformanceMetric]:
|
||||
@@ -106,7 +106,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
return await self.get_multi(
|
||||
filters={
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name
|
||||
"inventory_product_id": inventory_product_id
|
||||
},
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
@@ -116,7 +116,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to get metrics by tenant and product",
|
||||
tenant_id=tenant_id,
|
||||
product_name=product_name,
|
||||
inventory_product_id=inventory_product_id,
|
||||
error=str(e))
|
||||
raise DatabaseError(f"Failed to get metrics: {str(e)}")
|
||||
|
||||
@@ -172,7 +172,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
async def get_performance_trends(
|
||||
self,
|
||||
tenant_id: str,
|
||||
product_name: str = None,
|
||||
inventory_product_id: str = None,
|
||||
days: int = 30
|
||||
) -> Dict[str, Any]:
|
||||
"""Get performance trends for analysis"""
|
||||
@@ -184,13 +184,13 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
conditions = ["tenant_id = :tenant_id", "measured_at >= :start_date"]
|
||||
params = {"tenant_id": tenant_id, "start_date": start_date}
|
||||
|
||||
if product_name:
|
||||
conditions.append("product_name = :product_name")
|
||||
params["product_name"] = product_name
|
||||
if inventory_product_id:
|
||||
conditions.append("inventory_product_id = :inventory_product_id")
|
||||
params["inventory_product_id"] = inventory_product_id
|
||||
|
||||
query_text = f"""
|
||||
SELECT
|
||||
product_name,
|
||||
inventory_product_id,
|
||||
AVG(mae) as avg_mae,
|
||||
AVG(mse) as avg_mse,
|
||||
AVG(rmse) as avg_rmse,
|
||||
@@ -202,7 +202,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
MAX(measured_at) as last_measurement
|
||||
FROM model_performance_metrics
|
||||
WHERE {' AND '.join(conditions)}
|
||||
GROUP BY product_name
|
||||
GROUP BY inventory_product_id
|
||||
ORDER BY avg_accuracy DESC
|
||||
"""
|
||||
|
||||
@@ -211,7 +211,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
trends = []
|
||||
for row in result.fetchall():
|
||||
trends.append({
|
||||
"product_name": row.product_name,
|
||||
"inventory_product_id": row.inventory_product_id,
|
||||
"metrics": {
|
||||
"avg_mae": float(row.avg_mae) if row.avg_mae else None,
|
||||
"avg_mse": float(row.avg_mse) if row.avg_mse else None,
|
||||
@@ -230,7 +230,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name,
|
||||
"inventory_product_id": inventory_product_id,
|
||||
"trends": trends,
|
||||
"period_days": days,
|
||||
"total_products": len(trends)
|
||||
@@ -239,11 +239,11 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
except Exception as e:
|
||||
logger.error("Failed to get performance trends",
|
||||
tenant_id=tenant_id,
|
||||
product_name=product_name,
|
||||
inventory_product_id=inventory_product_id,
|
||||
error=str(e))
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"product_name": product_name,
|
||||
"inventory_product_id": inventory_product_id,
|
||||
"trends": [],
|
||||
"period_days": days,
|
||||
"total_products": 0
|
||||
@@ -268,16 +268,16 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
order_direction = "DESC" if order_desc else "ASC"
|
||||
|
||||
query_text = f"""
|
||||
SELECT DISTINCT ON (product_name, model_id)
|
||||
SELECT DISTINCT ON (inventory_product_id, model_id)
|
||||
model_id,
|
||||
product_name,
|
||||
inventory_product_id,
|
||||
{metric_type},
|
||||
measured_at,
|
||||
evaluation_samples
|
||||
FROM model_performance_metrics
|
||||
WHERE tenant_id = :tenant_id
|
||||
AND {metric_type} IS NOT NULL
|
||||
ORDER BY product_name, model_id, measured_at DESC, {metric_type} {order_direction}
|
||||
ORDER BY inventory_product_id, model_id, measured_at DESC, {metric_type} {order_direction}
|
||||
LIMIT :limit
|
||||
"""
|
||||
|
||||
@@ -290,7 +290,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
for row in result.fetchall():
|
||||
best_models.append({
|
||||
"model_id": row.model_id,
|
||||
"product_name": row.product_name,
|
||||
"inventory_product_id": row.inventory_product_id,
|
||||
"metric_value": float(getattr(row, metric_type)),
|
||||
"metric_type": metric_type,
|
||||
"measured_at": row.measured_at.isoformat() if row.measured_at else None,
|
||||
@@ -319,12 +319,12 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
# Get metrics by product using raw query
|
||||
product_query = text("""
|
||||
SELECT
|
||||
product_name,
|
||||
inventory_product_id,
|
||||
COUNT(*) as metric_count,
|
||||
AVG(accuracy_percentage) as avg_accuracy
|
||||
FROM model_performance_metrics
|
||||
WHERE tenant_id = :tenant_id
|
||||
GROUP BY product_name
|
||||
GROUP BY inventory_product_id
|
||||
ORDER BY avg_accuracy DESC
|
||||
""")
|
||||
|
||||
@@ -332,7 +332,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
product_stats = {}
|
||||
|
||||
for row in result.fetchall():
|
||||
product_stats[row.product_name] = {
|
||||
product_stats[row.inventory_product_id] = {
|
||||
"metric_count": row.metric_count,
|
||||
"avg_accuracy": float(row.avg_accuracy) if row.avg_accuracy else None
|
||||
}
|
||||
@@ -383,7 +383,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
query_text = f"""
|
||||
SELECT
|
||||
model_id,
|
||||
product_name,
|
||||
inventory_product_id,
|
||||
AVG({metric_type}) as avg_metric,
|
||||
MIN({metric_type}) as min_metric,
|
||||
MAX({metric_type}) as max_metric,
|
||||
@@ -392,7 +392,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
FROM model_performance_metrics
|
||||
WHERE model_id IN ('{model_ids_str}')
|
||||
AND {metric_type} IS NOT NULL
|
||||
GROUP BY model_id, product_name
|
||||
GROUP BY model_id, inventory_product_id
|
||||
ORDER BY avg_metric DESC
|
||||
"""
|
||||
|
||||
@@ -402,7 +402,7 @@ class PerformanceRepository(TrainingBaseRepository):
|
||||
for row in result.fetchall():
|
||||
comparisons.append({
|
||||
"model_id": row.model_id,
|
||||
"product_name": row.product_name,
|
||||
"inventory_product_id": row.inventory_product_id,
|
||||
"avg_metric": float(row.avg_metric),
|
||||
"min_metric": float(row.min_metric),
|
||||
"max_metric": float(row.max_metric),
|
||||
|
||||
Reference in New Issue
Block a user