Initial commit - production deployment
This commit is contained in:
6
services/production/app/schemas/__init__.py
Normal file
6
services/production/app/schemas/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# ================================================================
|
||||
# services/production/app/schemas/__init__.py
|
||||
# ================================================================
|
||||
"""
|
||||
Pydantic schemas for request/response models
|
||||
"""
|
||||
488
services/production/app/schemas/equipment.py
Normal file
488
services/production/app/schemas/equipment.py
Normal file
@@ -0,0 +1,488 @@
|
||||
# services/production/app/schemas/equipment.py
|
||||
"""
|
||||
Equipment schemas for Production Service
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from app.models.production import EquipmentType, EquipmentStatus, IoTProtocol, IoTConnectionStatus
|
||||
|
||||
|
||||
class IoTConnectionConfig(BaseModel):
|
||||
"""Schema for IoT connection configuration"""
|
||||
protocol: str = Field(..., description="IoT protocol (rest_api, opc_ua, mqtt, modbus, custom)")
|
||||
endpoint: str = Field(..., description="Connection endpoint (URL or IP address)")
|
||||
port: Optional[int] = Field(None, description="Connection port")
|
||||
username: Optional[str] = Field(None, description="Username for authentication")
|
||||
password: Optional[str] = Field(None, description="Password for authentication")
|
||||
api_key: Optional[str] = Field(None, description="API key for authentication")
|
||||
token: Optional[str] = Field(None, description="Authentication token")
|
||||
additional_config: Optional[dict] = Field(None, description="Additional protocol-specific configuration")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"protocol": "rest_api",
|
||||
"endpoint": "https://connectedcooking.com/api/v1",
|
||||
"port": 443,
|
||||
"api_key": "your-api-key-here",
|
||||
"additional_config": {"poll_interval": 30}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class EquipmentCreate(BaseModel):
|
||||
"""Schema for creating new equipment"""
|
||||
name: str = Field(..., min_length=1, max_length=255, description="Equipment name")
|
||||
type: EquipmentType = Field(..., description="Equipment type")
|
||||
model: Optional[str] = Field(None, max_length=100, description="Equipment model")
|
||||
serial_number: Optional[str] = Field(None, max_length=100, description="Serial number")
|
||||
location: Optional[str] = Field(None, max_length=255, description="Physical location")
|
||||
manufacturer: Optional[str] = Field(None, max_length=100, description="Manufacturer")
|
||||
firmware_version: Optional[str] = Field(None, max_length=50, description="Firmware version")
|
||||
status: EquipmentStatus = Field(default=EquipmentStatus.OPERATIONAL, description="Equipment status")
|
||||
|
||||
# Installation and maintenance
|
||||
install_date: Optional[datetime] = Field(None, description="Installation date")
|
||||
last_maintenance_date: Optional[datetime] = Field(None, description="Last maintenance date")
|
||||
next_maintenance_date: Optional[datetime] = Field(None, description="Next scheduled maintenance date")
|
||||
maintenance_interval_days: Optional[int] = Field(None, ge=1, description="Maintenance interval in days")
|
||||
|
||||
# Performance metrics
|
||||
efficiency_percentage: Optional[float] = Field(None, ge=0, le=100, description="Current efficiency percentage")
|
||||
uptime_percentage: Optional[float] = Field(None, ge=0, le=100, description="Overall uptime percentage")
|
||||
energy_usage_kwh: Optional[float] = Field(None, ge=0, description="Current energy usage in kWh")
|
||||
|
||||
# Specifications
|
||||
power_kw: Optional[float] = Field(None, ge=0, description="Power consumption in kilowatts")
|
||||
capacity: Optional[float] = Field(None, ge=0, description="Equipment capacity")
|
||||
weight_kg: Optional[float] = Field(None, ge=0, description="Weight in kilograms")
|
||||
|
||||
# Temperature monitoring
|
||||
current_temperature: Optional[float] = Field(None, description="Current temperature")
|
||||
target_temperature: Optional[float] = Field(None, description="Target temperature")
|
||||
|
||||
# IoT Connectivity
|
||||
iot_enabled: bool = Field(default=False, description="Enable IoT connectivity")
|
||||
iot_protocol: Optional[str] = Field(None, description="IoT protocol")
|
||||
iot_endpoint: Optional[str] = Field(None, description="IoT endpoint URL or IP")
|
||||
iot_port: Optional[int] = Field(None, description="IoT connection port")
|
||||
iot_config: Optional[dict] = Field(None, description="IoT configuration")
|
||||
|
||||
# Real-time monitoring
|
||||
supports_realtime: bool = Field(default=False, description="Supports real-time monitoring")
|
||||
poll_interval_seconds: Optional[int] = Field(None, ge=1, description="Polling interval in seconds")
|
||||
|
||||
# Sensor capabilities
|
||||
temperature_zones: Optional[int] = Field(None, ge=1, description="Number of temperature zones")
|
||||
supports_humidity: bool = Field(default=False, description="Supports humidity monitoring")
|
||||
supports_energy_monitoring: bool = Field(default=False, description="Supports energy monitoring")
|
||||
supports_remote_control: bool = Field(default=False, description="Supports remote control")
|
||||
|
||||
# Notes
|
||||
notes: Optional[str] = Field(None, description="Additional notes")
|
||||
|
||||
# Support contact information
|
||||
support_contact: Optional[dict] = Field(
|
||||
None,
|
||||
description="Support contact information for equipment maintenance",
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"email": "support@ovenfactory.com",
|
||||
"phone": "+1-800-555-1234",
|
||||
"company": "OvenTech Support",
|
||||
"contract_number": "SUP-2023-001",
|
||||
"response_time_sla": 24
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"name": "Horno Principal #1",
|
||||
"type": "oven",
|
||||
"model": "Miwe Condo CO 4.1212",
|
||||
"serial_number": "MCO-2021-001",
|
||||
"location": "Área de Horneado - Zona A",
|
||||
"status": "operational",
|
||||
"install_date": "2021-03-15T00:00:00Z",
|
||||
"maintenance_interval_days": 90,
|
||||
"efficiency_percentage": 92.0,
|
||||
"uptime_percentage": 98.5,
|
||||
"power_kw": 45.0,
|
||||
"capacity": 24.0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class EquipmentUpdate(BaseModel):
|
||||
"""Schema for updating equipment"""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
type: Optional[EquipmentType] = None
|
||||
model: Optional[str] = Field(None, max_length=100)
|
||||
serial_number: Optional[str] = Field(None, max_length=100)
|
||||
location: Optional[str] = Field(None, max_length=255)
|
||||
manufacturer: Optional[str] = Field(None, max_length=100)
|
||||
firmware_version: Optional[str] = Field(None, max_length=50)
|
||||
status: Optional[EquipmentStatus] = None
|
||||
|
||||
# Installation and maintenance
|
||||
install_date: Optional[datetime] = None
|
||||
last_maintenance_date: Optional[datetime] = None
|
||||
next_maintenance_date: Optional[datetime] = None
|
||||
maintenance_interval_days: Optional[int] = Field(None, ge=1)
|
||||
|
||||
# Performance metrics
|
||||
efficiency_percentage: Optional[float] = Field(None, ge=0, le=100)
|
||||
uptime_percentage: Optional[float] = Field(None, ge=0, le=100)
|
||||
energy_usage_kwh: Optional[float] = Field(None, ge=0)
|
||||
|
||||
# Specifications
|
||||
power_kw: Optional[float] = Field(None, ge=0)
|
||||
capacity: Optional[float] = Field(None, ge=0)
|
||||
weight_kg: Optional[float] = Field(None, ge=0)
|
||||
|
||||
# Temperature monitoring
|
||||
current_temperature: Optional[float] = None
|
||||
target_temperature: Optional[float] = None
|
||||
|
||||
# IoT Connectivity
|
||||
iot_enabled: Optional[bool] = None
|
||||
iot_protocol: Optional[str] = None
|
||||
iot_endpoint: Optional[str] = None
|
||||
iot_port: Optional[int] = None
|
||||
iot_config: Optional[dict] = None
|
||||
|
||||
# Real-time monitoring
|
||||
supports_realtime: Optional[bool] = None
|
||||
poll_interval_seconds: Optional[int] = Field(None, ge=1)
|
||||
|
||||
# Sensor capabilities
|
||||
temperature_zones: Optional[int] = Field(None, ge=1)
|
||||
supports_humidity: Optional[bool] = None
|
||||
supports_energy_monitoring: Optional[bool] = None
|
||||
supports_remote_control: Optional[bool] = None
|
||||
|
||||
# Notes
|
||||
notes: Optional[str] = None
|
||||
|
||||
# Support contact information
|
||||
support_contact: Optional[dict] = None
|
||||
|
||||
# Status flag
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"status": "maintenance",
|
||||
"last_maintenance_date": "2024-01-15T00:00:00Z",
|
||||
"next_maintenance_date": "2024-04-15T00:00:00Z",
|
||||
"efficiency_percentage": 88.0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class EquipmentResponse(BaseModel):
|
||||
"""Schema for equipment response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
name: str
|
||||
type: EquipmentType
|
||||
model: Optional[str] = None
|
||||
serial_number: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
manufacturer: Optional[str] = None
|
||||
firmware_version: Optional[str] = None
|
||||
status: EquipmentStatus
|
||||
|
||||
# Installation and maintenance
|
||||
install_date: Optional[datetime] = None
|
||||
last_maintenance_date: Optional[datetime] = None
|
||||
next_maintenance_date: Optional[datetime] = None
|
||||
maintenance_interval_days: Optional[int] = None
|
||||
|
||||
# Performance metrics
|
||||
efficiency_percentage: Optional[float] = None
|
||||
uptime_percentage: Optional[float] = None
|
||||
energy_usage_kwh: Optional[float] = None
|
||||
|
||||
# Specifications
|
||||
power_kw: Optional[float] = None
|
||||
capacity: Optional[float] = None
|
||||
weight_kg: Optional[float] = None
|
||||
|
||||
# Temperature monitoring
|
||||
current_temperature: Optional[float] = None
|
||||
target_temperature: Optional[float] = None
|
||||
|
||||
# IoT Connectivity
|
||||
iot_enabled: bool = False
|
||||
iot_protocol: Optional[str] = None
|
||||
iot_endpoint: Optional[str] = None
|
||||
iot_port: Optional[int] = None
|
||||
iot_connection_status: Optional[str] = None
|
||||
iot_last_connected: Optional[datetime] = None
|
||||
iot_config: Optional[dict] = None
|
||||
|
||||
# Real-time monitoring
|
||||
supports_realtime: bool = False
|
||||
poll_interval_seconds: Optional[int] = None
|
||||
|
||||
# Sensor capabilities
|
||||
temperature_zones: Optional[int] = None
|
||||
supports_humidity: bool = False
|
||||
supports_energy_monitoring: bool = False
|
||||
supports_remote_control: bool = False
|
||||
|
||||
# Status
|
||||
is_active: bool
|
||||
notes: Optional[str] = None
|
||||
|
||||
# Support contact information
|
||||
support_contact: Optional[dict] = None
|
||||
|
||||
# Timestamps
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class EquipmentListResponse(BaseModel):
|
||||
"""Schema for paginated equipment list response"""
|
||||
equipment: List[EquipmentResponse]
|
||||
total_count: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"equipment": [],
|
||||
"total_count": 10,
|
||||
"page": 1,
|
||||
"page_size": 50
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class EquipmentDeletionSummary(BaseModel):
|
||||
"""Schema for equipment deletion summary"""
|
||||
can_delete: bool = Field(..., description="Whether the equipment can be deleted")
|
||||
warnings: List[str] = Field(default_factory=list, description="List of warnings about deletion")
|
||||
production_batches_count: int = Field(default=0, description="Number of production batches using this equipment")
|
||||
maintenance_records_count: int = Field(default=0, description="Number of maintenance records")
|
||||
temperature_logs_count: int = Field(default=0, description="Number of temperature logs")
|
||||
equipment_name: Optional[str] = Field(None, description="Equipment name")
|
||||
equipment_type: Optional[str] = Field(None, description="Equipment type")
|
||||
equipment_location: Optional[str] = Field(None, description="Equipment location")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"can_delete": True,
|
||||
"warnings": ["3 production batch(es) are using this equipment"],
|
||||
"production_batches_count": 3,
|
||||
"maintenance_records_count": 5,
|
||||
"temperature_logs_count": 120,
|
||||
"equipment_name": "Horno Principal #1",
|
||||
"equipment_type": "oven",
|
||||
"equipment_location": "Área de Horneado"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# ================================================================
|
||||
# IoT-SPECIFIC SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class EquipmentSensorReadingResponse(BaseModel):
|
||||
"""Schema for equipment sensor reading response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
equipment_id: UUID
|
||||
batch_id: Optional[UUID] = None
|
||||
reading_time: datetime
|
||||
|
||||
# Temperature readings
|
||||
temperature: Optional[float] = None
|
||||
temperature_zones: Optional[dict] = None
|
||||
target_temperature: Optional[float] = None
|
||||
|
||||
# Humidity
|
||||
humidity: Optional[float] = None
|
||||
target_humidity: Optional[float] = None
|
||||
|
||||
# Energy monitoring
|
||||
energy_consumption_kwh: Optional[float] = None
|
||||
power_current_kw: Optional[float] = None
|
||||
|
||||
# Equipment status
|
||||
operational_status: Optional[str] = None
|
||||
cycle_stage: Optional[str] = None
|
||||
cycle_progress_percentage: Optional[float] = None
|
||||
time_remaining_minutes: Optional[int] = None
|
||||
|
||||
# Process parameters
|
||||
motor_speed_rpm: Optional[float] = None
|
||||
door_status: Optional[str] = None
|
||||
steam_level: Optional[float] = None
|
||||
|
||||
# Quality indicators
|
||||
product_weight_kg: Optional[float] = None
|
||||
moisture_content: Optional[float] = None
|
||||
|
||||
# Additional sensor data
|
||||
additional_sensors: Optional[dict] = None
|
||||
|
||||
# Data quality
|
||||
data_quality_score: Optional[float] = None
|
||||
is_anomaly: bool = False
|
||||
|
||||
created_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class EquipmentConnectionTestResponse(BaseModel):
|
||||
"""Schema for IoT connection test response"""
|
||||
success: bool = Field(..., description="Whether connection test succeeded")
|
||||
status: str = Field(..., description="Connection status")
|
||||
message: str = Field(..., description="Detailed message")
|
||||
response_time_ms: Optional[int] = Field(None, description="Response time in milliseconds")
|
||||
protocol_tested: str = Field(..., description="Protocol that was tested")
|
||||
endpoint_tested: str = Field(..., description="Endpoint that was tested")
|
||||
error_details: Optional[str] = Field(None, description="Error details if connection failed")
|
||||
supported_features: Optional[List[str]] = Field(None, description="List of supported IoT features")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"success": True,
|
||||
"status": "connected",
|
||||
"message": "Successfully connected to equipment",
|
||||
"response_time_ms": 145,
|
||||
"protocol_tested": "rest_api",
|
||||
"endpoint_tested": "https://connectedcooking.com/api/v1",
|
||||
"supported_features": ["temperature", "humidity", "energy_monitoring"]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class RealTimeDataResponse(BaseModel):
|
||||
"""Schema for real-time equipment data response"""
|
||||
equipment_id: UUID
|
||||
equipment_name: str
|
||||
timestamp: datetime
|
||||
connection_status: str
|
||||
|
||||
# Current readings
|
||||
temperature: Optional[float] = None
|
||||
temperature_zones: Optional[dict] = None
|
||||
humidity: Optional[float] = None
|
||||
energy_consumption_kwh: Optional[float] = None
|
||||
power_current_kw: Optional[float] = None
|
||||
|
||||
# Status
|
||||
operational_status: Optional[str] = None
|
||||
cycle_stage: Optional[str] = None
|
||||
cycle_progress_percentage: Optional[float] = None
|
||||
time_remaining_minutes: Optional[int] = None
|
||||
|
||||
# Active batch
|
||||
active_batch_id: Optional[UUID] = None
|
||||
active_batch_name: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"equipment_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"equipment_name": "Horno Principal #1",
|
||||
"timestamp": "2025-01-12T10:30:00Z",
|
||||
"connection_status": "connected",
|
||||
"temperature": 185.5,
|
||||
"temperature_zones": {"zone1": 180, "zone2": 190, "zone3": 185},
|
||||
"humidity": 65.0,
|
||||
"operational_status": "running",
|
||||
"cycle_stage": "baking",
|
||||
"cycle_progress_percentage": 45.0,
|
||||
"time_remaining_minutes": 12
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class EquipmentIoTAlertResponse(BaseModel):
|
||||
"""Schema for IoT alert response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
equipment_id: UUID
|
||||
batch_id: Optional[UUID] = None
|
||||
|
||||
# Alert information
|
||||
alert_type: str
|
||||
severity: str
|
||||
alert_time: datetime
|
||||
|
||||
# Alert details
|
||||
title: str
|
||||
message: str
|
||||
|
||||
# Threshold information
|
||||
threshold_value: Optional[float] = None
|
||||
actual_value: Optional[float] = None
|
||||
deviation_percentage: Optional[float] = None
|
||||
|
||||
# Status
|
||||
is_active: bool
|
||||
is_acknowledged: bool
|
||||
acknowledged_by: Optional[UUID] = None
|
||||
acknowledged_at: Optional[datetime] = None
|
||||
|
||||
is_resolved: bool
|
||||
resolved_by: Optional[UUID] = None
|
||||
resolved_at: Optional[datetime] = None
|
||||
resolution_notes: Optional[str] = None
|
||||
|
||||
# Automated response
|
||||
auto_resolved: bool
|
||||
corrective_action_taken: Optional[str] = None
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class EquipmentSensorHistoryResponse(BaseModel):
|
||||
"""Schema for sensor reading history response"""
|
||||
equipment_id: UUID
|
||||
equipment_name: str
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
total_readings: int
|
||||
readings: List[EquipmentSensorReadingResponse]
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"equipment_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"equipment_name": "Horno Principal #1",
|
||||
"start_time": "2025-01-12T08:00:00Z",
|
||||
"end_time": "2025-01-12T12:00:00Z",
|
||||
"total_readings": 48,
|
||||
"readings": []
|
||||
}
|
||||
}
|
||||
)
|
||||
352
services/production/app/schemas/production.py
Normal file
352
services/production/app/schemas/production.py
Normal file
@@ -0,0 +1,352 @@
|
||||
# ================================================================
|
||||
# services/production/app/schemas/production.py
|
||||
# ================================================================
|
||||
"""
|
||||
Pydantic schemas for production service
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from typing import Optional, List, Dict, Any, Union
|
||||
from datetime import datetime, date
|
||||
from uuid import UUID
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ProductionStatusEnum(str, Enum):
|
||||
"""Production batch status enumeration for API"""
|
||||
PENDING = "PENDING"
|
||||
IN_PROGRESS = "IN_PROGRESS"
|
||||
COMPLETED = "COMPLETED"
|
||||
CANCELLED = "CANCELLED"
|
||||
ON_HOLD = "ON_HOLD"
|
||||
QUALITY_CHECK = "QUALITY_CHECK"
|
||||
FAILED = "FAILED"
|
||||
|
||||
|
||||
class ProductionPriorityEnum(str, Enum):
|
||||
"""Production priority levels for API"""
|
||||
LOW = "LOW"
|
||||
MEDIUM = "MEDIUM"
|
||||
HIGH = "HIGH"
|
||||
URGENT = "URGENT"
|
||||
|
||||
|
||||
|
||||
|
||||
# ================================================================
|
||||
# PRODUCTION BATCH SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class ProductionBatchBase(BaseModel):
|
||||
"""Base schema for production batch"""
|
||||
product_id: UUID
|
||||
product_name: str = Field(..., min_length=1, max_length=255)
|
||||
recipe_id: Optional[UUID] = None
|
||||
planned_start_time: datetime
|
||||
planned_end_time: datetime
|
||||
planned_quantity: float = Field(..., gt=0)
|
||||
planned_duration_minutes: int = Field(..., gt=0)
|
||||
priority: ProductionPriorityEnum = ProductionPriorityEnum.MEDIUM
|
||||
is_rush_order: bool = False
|
||||
is_special_recipe: bool = False
|
||||
production_notes: Optional[str] = None
|
||||
|
||||
@validator('planned_end_time')
|
||||
def validate_end_time_after_start(cls, v, values):
|
||||
if 'planned_start_time' in values and v <= values['planned_start_time']:
|
||||
raise ValueError('planned_end_time must be after planned_start_time')
|
||||
return v
|
||||
|
||||
|
||||
class ProductionBatchCreate(ProductionBatchBase):
|
||||
"""Schema for creating a production batch"""
|
||||
batch_number: Optional[str] = Field(None, max_length=50)
|
||||
order_id: Optional[UUID] = None
|
||||
forecast_id: Optional[UUID] = None
|
||||
equipment_used: Optional[List[str]] = None
|
||||
staff_assigned: Optional[List[str]] = None
|
||||
station_id: Optional[str] = Field(None, max_length=50)
|
||||
|
||||
|
||||
class ProductionBatchUpdate(BaseModel):
|
||||
"""Schema for updating a production batch"""
|
||||
product_name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
planned_start_time: Optional[datetime] = None
|
||||
planned_end_time: Optional[datetime] = None
|
||||
planned_quantity: Optional[float] = Field(None, gt=0)
|
||||
planned_duration_minutes: Optional[int] = Field(None, gt=0)
|
||||
actual_quantity: Optional[float] = Field(None, ge=0)
|
||||
priority: Optional[ProductionPriorityEnum] = None
|
||||
equipment_used: Optional[List[str]] = None
|
||||
staff_assigned: Optional[List[str]] = None
|
||||
station_id: Optional[str] = Field(None, max_length=50)
|
||||
production_notes: Optional[str] = None
|
||||
|
||||
|
||||
class ProductionBatchStatusUpdate(BaseModel):
|
||||
"""Schema for updating production batch status"""
|
||||
status: ProductionStatusEnum
|
||||
actual_quantity: Optional[float] = Field(None, ge=0)
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class ProductionBatchResponse(BaseModel):
|
||||
"""Schema for production batch response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
batch_number: str
|
||||
product_id: UUID
|
||||
product_name: str
|
||||
recipe_id: Optional[UUID]
|
||||
planned_start_time: datetime
|
||||
planned_end_time: datetime
|
||||
planned_quantity: float
|
||||
planned_duration_minutes: int
|
||||
actual_start_time: Optional[datetime]
|
||||
actual_end_time: Optional[datetime]
|
||||
actual_quantity: Optional[float]
|
||||
actual_duration_minutes: Optional[int]
|
||||
status: ProductionStatusEnum
|
||||
priority: ProductionPriorityEnum
|
||||
|
||||
# Process stage tracking (added to replace frontend mock data)
|
||||
current_process_stage: Optional[str] = None
|
||||
process_stage_history: Optional[List[Dict[str, Any]]] = None
|
||||
pending_quality_checks: Optional[List[Dict[str, Any]]] = None
|
||||
completed_quality_checks: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
estimated_cost: Optional[float]
|
||||
actual_cost: Optional[float]
|
||||
yield_percentage: Optional[float]
|
||||
quality_score: Optional[float]
|
||||
equipment_used: Optional[List[str]]
|
||||
staff_assigned: Optional[List[str]]
|
||||
station_id: Optional[str]
|
||||
order_id: Optional[UUID]
|
||||
forecast_id: Optional[UUID]
|
||||
is_rush_order: bool
|
||||
is_special_recipe: bool
|
||||
production_notes: Optional[str]
|
||||
quality_notes: Optional[str]
|
||||
delay_reason: Optional[str]
|
||||
cancellation_reason: Optional[str]
|
||||
reasoning_data: Optional[Dict[str, Any]] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
completed_at: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ================================================================
|
||||
# PRODUCTION SCHEDULE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class ProductionScheduleBase(BaseModel):
|
||||
"""Base schema for production schedule"""
|
||||
schedule_date: date
|
||||
shift_start: datetime
|
||||
shift_end: datetime
|
||||
total_capacity_hours: float = Field(..., gt=0)
|
||||
planned_capacity_hours: float = Field(..., gt=0)
|
||||
staff_count: int = Field(..., gt=0)
|
||||
equipment_capacity: Optional[Dict[str, Any]] = None
|
||||
station_assignments: Optional[Dict[str, Any]] = None
|
||||
schedule_notes: Optional[str] = None
|
||||
|
||||
@validator('shift_end')
|
||||
def validate_shift_end_after_start(cls, v, values):
|
||||
if 'shift_start' in values and v <= values['shift_start']:
|
||||
raise ValueError('shift_end must be after shift_start')
|
||||
return v
|
||||
|
||||
@validator('planned_capacity_hours')
|
||||
def validate_planned_capacity(cls, v, values):
|
||||
if 'total_capacity_hours' in values and v > values['total_capacity_hours']:
|
||||
raise ValueError('planned_capacity_hours cannot exceed total_capacity_hours')
|
||||
return v
|
||||
|
||||
|
||||
class ProductionScheduleCreate(ProductionScheduleBase):
|
||||
"""Schema for creating a production schedule"""
|
||||
pass
|
||||
|
||||
|
||||
class ProductionScheduleUpdate(BaseModel):
|
||||
"""Schema for updating a production schedule"""
|
||||
shift_start: Optional[datetime] = None
|
||||
shift_end: Optional[datetime] = None
|
||||
total_capacity_hours: Optional[float] = Field(None, gt=0)
|
||||
planned_capacity_hours: Optional[float] = Field(None, gt=0)
|
||||
staff_count: Optional[int] = Field(None, gt=0)
|
||||
overtime_hours: Optional[float] = Field(None, ge=0)
|
||||
equipment_capacity: Optional[Dict[str, Any]] = None
|
||||
station_assignments: Optional[Dict[str, Any]] = None
|
||||
schedule_notes: Optional[str] = None
|
||||
|
||||
|
||||
class ProductionScheduleResponse(BaseModel):
|
||||
"""Schema for production schedule response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
schedule_date: date
|
||||
shift_start: datetime
|
||||
shift_end: datetime
|
||||
total_capacity_hours: float
|
||||
planned_capacity_hours: float
|
||||
actual_capacity_hours: Optional[float]
|
||||
overtime_hours: Optional[float]
|
||||
staff_count: int
|
||||
equipment_capacity: Optional[Dict[str, Any]]
|
||||
station_assignments: Optional[Dict[str, Any]]
|
||||
total_batches_planned: int
|
||||
total_batches_completed: Optional[int]
|
||||
total_quantity_planned: float
|
||||
total_quantity_produced: Optional[float]
|
||||
is_finalized: bool
|
||||
is_active: bool
|
||||
efficiency_percentage: Optional[float]
|
||||
utilization_percentage: Optional[float]
|
||||
on_time_completion_rate: Optional[float]
|
||||
schedule_notes: Optional[str]
|
||||
schedule_adjustments: Optional[Dict[str, Any]]
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
finalized_at: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ================================================================
|
||||
# QUALITY CHECK SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class QualityCheckBase(BaseModel):
|
||||
"""Base schema for quality check"""
|
||||
batch_id: UUID
|
||||
check_type: str = Field(..., min_length=1, max_length=50)
|
||||
check_time: datetime
|
||||
quality_score: float = Field(..., ge=1, le=10)
|
||||
pass_fail: bool
|
||||
defect_count: int = Field(0, ge=0)
|
||||
defect_types: Optional[List[str]] = None
|
||||
check_notes: Optional[str] = None
|
||||
|
||||
|
||||
class QualityCheckCreate(QualityCheckBase):
|
||||
"""Schema for creating a quality check"""
|
||||
checker_id: Optional[str] = Field(None, max_length=100)
|
||||
measured_weight: Optional[float] = Field(None, gt=0)
|
||||
measured_temperature: Optional[float] = None
|
||||
measured_moisture: Optional[float] = Field(None, ge=0, le=100)
|
||||
measured_dimensions: Optional[Dict[str, float]] = None
|
||||
target_weight: Optional[float] = Field(None, gt=0)
|
||||
target_temperature: Optional[float] = None
|
||||
target_moisture: Optional[float] = Field(None, ge=0, le=100)
|
||||
tolerance_percentage: Optional[float] = Field(None, ge=0, le=100)
|
||||
corrective_actions: Optional[List[str]] = None
|
||||
|
||||
|
||||
class QualityCheckResponse(BaseModel):
|
||||
"""Schema for quality check response"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
batch_id: UUID
|
||||
check_type: str
|
||||
check_time: datetime
|
||||
checker_id: Optional[str]
|
||||
quality_score: float
|
||||
pass_fail: bool
|
||||
defect_count: int
|
||||
defect_types: Optional[List[str]]
|
||||
measured_weight: Optional[float]
|
||||
measured_temperature: Optional[float]
|
||||
measured_moisture: Optional[float]
|
||||
measured_dimensions: Optional[Dict[str, float]]
|
||||
target_weight: Optional[float]
|
||||
target_temperature: Optional[float]
|
||||
target_moisture: Optional[float]
|
||||
tolerance_percentage: Optional[float]
|
||||
within_tolerance: Optional[bool]
|
||||
corrective_action_needed: bool
|
||||
corrective_actions: Optional[List[str]]
|
||||
check_notes: Optional[str]
|
||||
photos_urls: Optional[List[str]]
|
||||
certificate_url: Optional[str]
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
|
||||
|
||||
# ================================================================
|
||||
# DASHBOARD AND ANALYTICS SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class ProductionDashboardSummary(BaseModel):
|
||||
"""Schema for production dashboard summary"""
|
||||
active_batches: int
|
||||
todays_production_plan: List[Dict[str, Any]]
|
||||
capacity_utilization: float
|
||||
on_time_completion_rate: float
|
||||
average_quality_score: float
|
||||
total_output_today: float
|
||||
efficiency_percentage: float
|
||||
|
||||
|
||||
class DailyProductionRequirements(BaseModel):
|
||||
"""Schema for daily production requirements"""
|
||||
date: date
|
||||
production_plan: List[Dict[str, Any]]
|
||||
total_capacity_needed: float
|
||||
available_capacity: float
|
||||
capacity_gap: float
|
||||
urgent_items: int
|
||||
recommended_schedule: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class ProductionMetrics(BaseModel):
|
||||
"""Schema for production metrics"""
|
||||
period_start: date
|
||||
period_end: date
|
||||
total_batches: int
|
||||
completed_batches: int
|
||||
completion_rate: float
|
||||
average_yield_percentage: float
|
||||
on_time_completion_rate: float
|
||||
total_production_cost: float
|
||||
average_quality_score: float
|
||||
efficiency_trends: List[Dict[str, Any]]
|
||||
|
||||
|
||||
# ================================================================
|
||||
# REQUEST/RESPONSE WRAPPERS
|
||||
# ================================================================
|
||||
|
||||
class ProductionBatchListResponse(BaseModel):
|
||||
"""Schema for production batch list response"""
|
||||
batches: List[ProductionBatchResponse]
|
||||
total_count: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
class ProductionScheduleListResponse(BaseModel):
|
||||
"""Schema for production schedule list response"""
|
||||
schedules: List[ProductionScheduleResponse]
|
||||
total_count: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
class QualityCheckListResponse(BaseModel):
|
||||
"""Schema for quality check list response"""
|
||||
quality_checks: List[QualityCheckResponse]
|
||||
total_count: int
|
||||
page: int
|
||||
page_size: int
|
||||
180
services/production/app/schemas/quality_templates.py
Normal file
180
services/production/app/schemas/quality_templates.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# services/production/app/schemas/quality_templates.py
|
||||
"""
|
||||
Quality Check Template Pydantic schemas for validation and serialization
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from typing import Optional, List, Dict, Any, Union
|
||||
from uuid import UUID
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from ..models.production import ProcessStage
|
||||
|
||||
|
||||
class QualityCheckType(str, Enum):
|
||||
"""Quality check types"""
|
||||
VISUAL = "visual"
|
||||
MEASUREMENT = "measurement"
|
||||
TEMPERATURE = "temperature"
|
||||
WEIGHT = "weight"
|
||||
BOOLEAN = "boolean"
|
||||
TIMING = "timing"
|
||||
CHECKLIST = "checklist"
|
||||
|
||||
|
||||
class QualityCheckTemplateBase(BaseModel):
|
||||
"""Base schema for quality check templates"""
|
||||
name: str = Field(..., min_length=1, max_length=255, description="Template name")
|
||||
template_code: Optional[str] = Field(None, max_length=100, description="Template code for reference")
|
||||
check_type: QualityCheckType = Field(..., description="Type of quality check")
|
||||
category: Optional[str] = Field(None, max_length=100, description="Check category (e.g., appearance, structure)")
|
||||
description: Optional[str] = Field(None, description="Template description")
|
||||
instructions: Optional[str] = Field(None, description="Check instructions for staff")
|
||||
|
||||
# Configuration
|
||||
parameters: Optional[Dict[str, Any]] = Field(None, description="Dynamic check parameters")
|
||||
thresholds: Optional[Dict[str, Any]] = Field(None, description="Pass/fail criteria")
|
||||
scoring_criteria: Optional[Dict[str, Any]] = Field(None, description="Scoring methodology")
|
||||
|
||||
# Settings
|
||||
is_active: bool = Field(True, description="Whether template is active")
|
||||
is_required: bool = Field(False, description="Whether check is required")
|
||||
is_critical: bool = Field(False, description="Whether failure blocks production")
|
||||
weight: float = Field(1.0, ge=0.0, le=10.0, description="Weight in overall quality score")
|
||||
|
||||
# Measurement specifications
|
||||
min_value: Optional[float] = Field(None, description="Minimum acceptable value")
|
||||
max_value: Optional[float] = Field(None, description="Maximum acceptable value")
|
||||
target_value: Optional[float] = Field(None, description="Target value")
|
||||
unit: Optional[str] = Field(None, max_length=20, description="Unit of measurement")
|
||||
tolerance_percentage: Optional[float] = Field(None, ge=0.0, le=100.0, description="Tolerance percentage")
|
||||
|
||||
# Process stage applicability
|
||||
applicable_stages: Optional[List[ProcessStage]] = Field(None, description="Applicable process stages")
|
||||
|
||||
@validator('applicable_stages')
|
||||
def validate_stages(cls, v):
|
||||
if v is not None:
|
||||
# Ensure all values are valid ProcessStage enums
|
||||
for stage in v:
|
||||
if stage not in ProcessStage:
|
||||
raise ValueError(f"Invalid process stage: {stage}")
|
||||
return v
|
||||
|
||||
@validator('min_value', 'max_value', 'target_value')
|
||||
def validate_measurement_values(cls, v, values):
|
||||
if v is not None and values.get('check_type') not in [QualityCheckType.MEASUREMENT, QualityCheckType.TEMPERATURE, QualityCheckType.WEIGHT]:
|
||||
return None # Clear values for non-measurement types
|
||||
return v
|
||||
|
||||
|
||||
class QualityCheckTemplateCreate(QualityCheckTemplateBase):
|
||||
"""Schema for creating quality check templates"""
|
||||
created_by: UUID = Field(..., description="User ID who created the template")
|
||||
|
||||
|
||||
class QualityCheckTemplateUpdate(BaseModel):
|
||||
"""Schema for updating quality check templates"""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
template_code: Optional[str] = Field(None, max_length=100)
|
||||
check_type: Optional[QualityCheckType] = None
|
||||
category: Optional[str] = Field(None, max_length=100)
|
||||
description: Optional[str] = None
|
||||
instructions: Optional[str] = None
|
||||
parameters: Optional[Dict[str, Any]] = None
|
||||
thresholds: Optional[Dict[str, Any]] = None
|
||||
scoring_criteria: Optional[Dict[str, Any]] = None
|
||||
is_active: Optional[bool] = None
|
||||
is_required: Optional[bool] = None
|
||||
is_critical: Optional[bool] = None
|
||||
weight: Optional[float] = Field(None, ge=0.0, le=10.0)
|
||||
min_value: Optional[float] = None
|
||||
max_value: Optional[float] = None
|
||||
target_value: Optional[float] = None
|
||||
unit: Optional[str] = Field(None, max_length=20)
|
||||
tolerance_percentage: Optional[float] = Field(None, ge=0.0, le=100.0)
|
||||
applicable_stages: Optional[List[ProcessStage]] = None
|
||||
|
||||
|
||||
class QualityCheckTemplateResponse(QualityCheckTemplateBase):
|
||||
"""Schema for quality check template responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
created_by: UUID
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class QualityCheckTemplateList(BaseModel):
|
||||
"""Schema for paginated quality check template lists"""
|
||||
templates: List[QualityCheckTemplateResponse]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
class QualityCheckCriterion(BaseModel):
|
||||
"""Individual quality check criterion within a template"""
|
||||
id: str = Field(..., description="Unique criterion identifier")
|
||||
name: str = Field(..., description="Criterion name")
|
||||
description: str = Field(..., description="Criterion description")
|
||||
check_type: QualityCheckType = Field(..., description="Type of check")
|
||||
required: bool = Field(True, description="Whether criterion is required")
|
||||
weight: float = Field(1.0, ge=0.0, le=10.0, description="Weight in template score")
|
||||
acceptable_criteria: str = Field(..., description="Description of acceptable criteria")
|
||||
min_value: Optional[float] = None
|
||||
max_value: Optional[float] = None
|
||||
unit: Optional[str] = None
|
||||
is_critical: bool = Field(False, description="Whether failure is critical")
|
||||
|
||||
|
||||
class QualityCheckResult(BaseModel):
|
||||
"""Result of a quality check criterion"""
|
||||
criterion_id: str = Field(..., description="Criterion identifier")
|
||||
value: Union[float, str, bool] = Field(..., description="Check result value")
|
||||
score: float = Field(..., ge=0.0, le=10.0, description="Score for this criterion")
|
||||
notes: Optional[str] = Field(None, description="Additional notes")
|
||||
photos: Optional[List[str]] = Field(None, description="Photo URLs")
|
||||
pass_check: bool = Field(..., description="Whether criterion passed")
|
||||
timestamp: datetime = Field(..., description="When check was performed")
|
||||
|
||||
|
||||
class QualityCheckExecutionRequest(BaseModel):
|
||||
"""Schema for executing a quality check using a template"""
|
||||
template_id: UUID = Field(..., description="Quality check template ID")
|
||||
batch_id: UUID = Field(..., description="Production batch ID")
|
||||
process_stage: ProcessStage = Field(..., description="Current process stage")
|
||||
checker_id: Optional[str] = Field(None, description="Staff member performing check")
|
||||
results: List[QualityCheckResult] = Field(..., description="Check results")
|
||||
final_notes: Optional[str] = Field(None, description="Final notes")
|
||||
photos: Optional[List[str]] = Field(None, description="Additional photo URLs")
|
||||
|
||||
|
||||
class QualityCheckExecutionResponse(BaseModel):
|
||||
"""Schema for quality check execution results"""
|
||||
check_id: UUID = Field(..., description="Created quality check ID")
|
||||
overall_score: float = Field(..., ge=0.0, le=10.0, description="Overall quality score")
|
||||
overall_pass: bool = Field(..., description="Whether check passed overall")
|
||||
critical_failures: List[str] = Field(..., description="List of critical failures")
|
||||
corrective_actions: List[str] = Field(..., description="Recommended corrective actions")
|
||||
timestamp: datetime = Field(..., description="When check was completed")
|
||||
|
||||
|
||||
class ProcessStageQualityConfig(BaseModel):
|
||||
"""Configuration for quality checks at a specific process stage"""
|
||||
stage: ProcessStage = Field(..., description="Process stage")
|
||||
template_ids: List[UUID] = Field(..., description="Required template IDs")
|
||||
custom_parameters: Optional[Dict[str, Any]] = Field(None, description="Stage-specific parameters")
|
||||
is_required: bool = Field(True, description="Whether stage requires quality checks")
|
||||
blocking: bool = Field(True, description="Whether stage blocks on failed checks")
|
||||
|
||||
|
||||
class RecipeQualityConfiguration(BaseModel):
|
||||
"""Quality check configuration for a recipe"""
|
||||
stages: Dict[str, ProcessStageQualityConfig] = Field(..., description="Stage configurations")
|
||||
global_parameters: Optional[Dict[str, Any]] = Field(None, description="Global quality parameters")
|
||||
default_templates: Optional[List[UUID]] = Field(None, description="Default template IDs")
|
||||
Reference in New Issue
Block a user