Add more services
This commit is contained in:
271
shared/clients/recipes_client.py
Normal file
271
shared/clients/recipes_client.py
Normal file
@@ -0,0 +1,271 @@
|
||||
# shared/clients/recipes_client.py
|
||||
"""
|
||||
Recipes Service Client for Inter-Service Communication
|
||||
Provides access to recipe and ingredient requirements from other services
|
||||
"""
|
||||
|
||||
import structlog
|
||||
from typing import Dict, Any, Optional, List
|
||||
from uuid import UUID
|
||||
from shared.clients.base_service_client import BaseServiceClient
|
||||
from shared.config.base import BaseServiceSettings
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
class RecipesServiceClient(BaseServiceClient):
|
||||
"""Client for communicating with the Recipes Service"""
|
||||
|
||||
def __init__(self, config: BaseServiceSettings):
|
||||
super().__init__("recipes", config)
|
||||
|
||||
def get_service_base_path(self) -> str:
|
||||
return "/api/v1"
|
||||
|
||||
# ================================================================
|
||||
# RECIPE MANAGEMENT
|
||||
# ================================================================
|
||||
|
||||
async def get_recipe_by_id(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get recipe details by ID"""
|
||||
try:
|
||||
result = await self.get(f"recipes/{recipe_id}", tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Retrieved recipe details from recipes service",
|
||||
recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipe details",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def get_recipes_by_product_ids(self, tenant_id: str, product_ids: List[str]) -> Optional[List[Dict[str, Any]]]:
|
||||
"""Get recipes for multiple products"""
|
||||
try:
|
||||
params = {"product_ids": ",".join(product_ids)}
|
||||
result = await self.get("recipes/by-products", tenant_id=tenant_id, params=params)
|
||||
recipes = result.get('recipes', []) if result else []
|
||||
logger.info("Retrieved recipes by product IDs from recipes service",
|
||||
product_ids_count=len(product_ids),
|
||||
recipes_count=len(recipes),
|
||||
tenant_id=tenant_id)
|
||||
return recipes
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipes by product IDs",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return []
|
||||
|
||||
async def get_all_recipes(self, tenant_id: str, is_active: Optional[bool] = True) -> Optional[List[Dict[str, Any]]]:
|
||||
"""Get all recipes for a tenant"""
|
||||
try:
|
||||
params = {}
|
||||
if is_active is not None:
|
||||
params["is_active"] = is_active
|
||||
|
||||
result = await self.get_paginated("recipes", tenant_id=tenant_id, params=params)
|
||||
logger.info("Retrieved all recipes from recipes service",
|
||||
recipes_count=len(result), tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting all recipes",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return []
|
||||
|
||||
# ================================================================
|
||||
# INGREDIENT REQUIREMENTS
|
||||
# ================================================================
|
||||
|
||||
async def get_recipe_requirements(self, tenant_id: str, recipe_ids: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
||||
"""Get ingredient requirements for recipes"""
|
||||
try:
|
||||
params = {}
|
||||
if recipe_ids:
|
||||
params["recipe_ids"] = ",".join(recipe_ids)
|
||||
|
||||
result = await self.get("requirements", tenant_id=tenant_id, params=params)
|
||||
if result:
|
||||
logger.info("Retrieved recipe requirements from recipes service",
|
||||
recipe_ids_count=len(recipe_ids) if recipe_ids else 0,
|
||||
tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipe requirements",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def get_ingredient_requirements(self, tenant_id: str, product_ids: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
||||
"""Get ingredient requirements for production planning"""
|
||||
try:
|
||||
params = {}
|
||||
if product_ids:
|
||||
params["product_ids"] = ",".join(product_ids)
|
||||
|
||||
result = await self.get("ingredient-requirements", tenant_id=tenant_id, params=params)
|
||||
if result:
|
||||
logger.info("Retrieved ingredient requirements from recipes service",
|
||||
product_ids_count=len(product_ids) if product_ids else 0,
|
||||
tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting ingredient requirements",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def calculate_ingredients_for_quantity(self, tenant_id: str, recipe_id: str, quantity: float) -> Optional[Dict[str, Any]]:
|
||||
"""Calculate ingredient quantities needed for a specific production quantity"""
|
||||
try:
|
||||
data = {
|
||||
"recipe_id": recipe_id,
|
||||
"quantity": quantity
|
||||
}
|
||||
result = await self.post("calculate-ingredients", data=data, tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Calculated ingredient quantities from recipes service",
|
||||
recipe_id=recipe_id, quantity=quantity, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error calculating ingredient quantities",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def calculate_batch_ingredients(self, tenant_id: str, production_requests: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""Calculate total ingredient requirements for multiple production batches"""
|
||||
try:
|
||||
data = {"production_requests": production_requests}
|
||||
result = await self.post("calculate-batch-ingredients", data=data, tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Calculated batch ingredient requirements from recipes service",
|
||||
batches_count=len(production_requests), tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error calculating batch ingredient requirements",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
# ================================================================
|
||||
# PRODUCTION SUPPORT
|
||||
# ================================================================
|
||||
|
||||
async def get_production_instructions(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get detailed production instructions for a recipe"""
|
||||
try:
|
||||
result = await self.get(f"recipes/{recipe_id}/production-instructions", tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Retrieved production instructions from recipes service",
|
||||
recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting production instructions",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def get_recipe_yield_info(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get yield information for a recipe"""
|
||||
try:
|
||||
result = await self.get(f"recipes/{recipe_id}/yield", tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Retrieved recipe yield info from recipes service",
|
||||
recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipe yield info",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def validate_recipe_feasibility(self, tenant_id: str, recipe_id: str, quantity: float) -> Optional[Dict[str, Any]]:
|
||||
"""Validate if a recipe can be produced in the requested quantity"""
|
||||
try:
|
||||
data = {
|
||||
"recipe_id": recipe_id,
|
||||
"quantity": quantity
|
||||
}
|
||||
result = await self.post("validate-feasibility", data=data, tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Validated recipe feasibility from recipes service",
|
||||
recipe_id=recipe_id, quantity=quantity, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error validating recipe feasibility",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
# ================================================================
|
||||
# ANALYTICS AND OPTIMIZATION
|
||||
# ================================================================
|
||||
|
||||
async def get_recipe_cost_analysis(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get cost analysis for a recipe"""
|
||||
try:
|
||||
result = await self.get(f"recipes/{recipe_id}/cost-analysis", tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Retrieved recipe cost analysis from recipes service",
|
||||
recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipe cost analysis",
|
||||
error=str(e), recipe_id=recipe_id, tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def optimize_production_batch(self, tenant_id: str, requirements: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""Optimize production batch to minimize waste and cost"""
|
||||
try:
|
||||
data = {"requirements": requirements}
|
||||
result = await self.post("optimize-batch", data=data, tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Optimized production batch from recipes service",
|
||||
requirements_count=len(requirements), tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error optimizing production batch",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
# ================================================================
|
||||
# DASHBOARD AND ANALYTICS
|
||||
# ================================================================
|
||||
|
||||
async def get_dashboard_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get recipes dashboard summary data"""
|
||||
try:
|
||||
result = await self.get("dashboard-summary", tenant_id=tenant_id)
|
||||
if result:
|
||||
logger.info("Retrieved recipes dashboard summary",
|
||||
tenant_id=tenant_id)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Error getting recipes dashboard summary",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return None
|
||||
|
||||
async def get_popular_recipes(self, tenant_id: str, period: str = "last_30_days") -> Optional[List[Dict[str, Any]]]:
|
||||
"""Get most popular recipes based on production frequency"""
|
||||
try:
|
||||
params = {"period": period}
|
||||
result = await self.get("popular-recipes", tenant_id=tenant_id, params=params)
|
||||
recipes = result.get('recipes', []) if result else []
|
||||
logger.info("Retrieved popular recipes from recipes service",
|
||||
period=period, recipes_count=len(recipes), tenant_id=tenant_id)
|
||||
return recipes
|
||||
except Exception as e:
|
||||
logger.error("Error getting popular recipes",
|
||||
error=str(e), tenant_id=tenant_id)
|
||||
return []
|
||||
|
||||
# ================================================================
|
||||
# UTILITY METHODS
|
||||
# ================================================================
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
"""Check if recipes service is healthy"""
|
||||
try:
|
||||
result = await self.get("../health") # Health endpoint is not tenant-scoped
|
||||
return result is not None
|
||||
except Exception as e:
|
||||
logger.error("Recipes service health check failed", error=str(e))
|
||||
return False
|
||||
|
||||
|
||||
# Factory function for dependency injection
|
||||
def create_recipes_client(config: BaseServiceSettings) -> RecipesServiceClient:
|
||||
"""Create recipes service client instance"""
|
||||
return RecipesServiceClient(config)
|
||||
Reference in New Issue
Block a user