import datetime # Added full datetime module import import logging # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def predict_next_service(last_service_date, usage_hours, days_per_week, equipment_type): """ Predict the next AMC (Annual Maintenance Contract) date based on usage and equipment type. Args: last_service_date (datetime.date): The date of the last service. usage_hours (int/float): Average usage hours per day. days_per_week (int): Number of operational days per week. equipment_type (str): Type of equipment (e.g., 'Analyzer', 'Centrifuge', 'Incubator'). Returns: datetime.date: The predicted next service date. Raises: ValueError: If inputs are invalid (e.g., negative usage hours, invalid equipment type). """ try: # Validate inputs if not isinstance(last_service_date, datetime.date): raise ValueError("last_service_date must be a datetime.date object") if not isinstance(usage_hours, (int, float)) or usage_hours <= 0: raise ValueError("usage_hours must be a positive number") if not isinstance(days_per_week, int) or not 1 <= days_per_week <= 7: raise ValueError("days_per_week must be an integer between 1 and 7") if not isinstance(equipment_type, str): raise ValueError("equipment_type must be a string") # Log input parameters logger.info( f"Predicting next service: last_service_date={last_service_date}, " f"usage_hours={usage_hours}, days_per_week={days_per_week}, equipment_type={equipment_type}" ) # Usage intensity score usage_score = usage_hours * days_per_week # Base interval in days base_interval = 180 # Standard maintenance interval (6 months) # Adjust interval based on usage if usage_score >= 120: interval = 90 # High usage: 3 months elif usage_score >= 80: interval = 120 # Medium-high usage: 4 months elif usage_score >= 40: interval = 150 # Medium usage: 5 months else: interval = 180 # Low usage: 6 months # Equipment type adjustments (days to add/subtract from interval) equipment_adjustments = { "Analyzer": -15, # More sensitive, service sooner "Centrifuge": -15, # More sensitive, service sooner "Incubator": 15, # More stable, service later "Generic": 0 # No adjustment for generic equipment } # Apply equipment type adjustment adjustment = equipment_adjustments.get(equipment_type, 0) if adjustment == 0 and equipment_type not in equipment_adjustments: logger.warning(f"Unknown equipment type '{equipment_type}', using default adjustment (0 days)") interval += adjustment # Ensure interval is positive if interval <= 0: raise ValueError(f"Calculated interval ({interval} days) is invalid; check equipment type adjustments") # Calculate next service date next_date = last_service_date + datetime.timedelta(days=interval) # Updated to use datetime.timedelta logger.info(f"Predicted next service date: {next_date}") return next_date except Exception as e: logger.error(f"Error in predict_next_service: {e}") raise