2026-01-26 10:30:49 -05:00

203 lines
7.4 KiB
Python

"""
Factory for creating Service objects.
"""
from datetime import datetime, date, timedelta
from typing import List, Dict, Any, Optional
from backend.core.models import Schedule, Service, Profile
from backend.core.utils.helpers import get_month_start_end, dict_to_json
class ServiceFactory:
"""
Factory for creating Service objects based on schedules.
"""
@staticmethod
def serialize_service(service: Service) -> Dict[str, Any]:
"""
Convert a Service object to a serializable dictionary.
Args:
service: The Service object to serialize
Returns:
Dict[str, Any]: A dictionary representation of the Service object
"""
return {
'id': str(service.id),
'account_id': str(service.account.id),
'account_name': service.account.name,
'date': service.date.isoformat(),
'status': service.status,
'notes': service.notes,
'deadline_start': service.deadline_start.isoformat(),
'deadline_end': service.deadline_end.isoformat(),
'created_at': service.created_at.isoformat(),
'updated_at': service.updated_at.isoformat(),
'completed_at': service.completed_at.isoformat() if service.completed_at else None,
'team_member_ids': [str(member.id) for member in service.team_members.all()],
'team_member_names': service.team_member_names
}
@classmethod
def create_service(cls, schedule: Schedule, service_date: date, is_weekend: bool = False) -> Service:
"""
Create a Service object for a specific date based on a schedule.
Args:
schedule: The schedule to base the service on
service_date: The date for the service
is_weekend: Whether this is a weekend service
Returns:
Service: The created service object
"""
# Set default deadline times (6:00pm the day of service to 6:00am the following day)
deadline_start = datetime.combine(service_date, datetime.min.time()).replace(hour=18) # 6:00pm
deadline_end = datetime.combine(service_date + timedelta(days=1), datetime.min.time()).replace(hour=6) # 6:00am next day
notes = None
if is_weekend:
notes = "Weekend Service"
# Create the service
service = Service.objects.create(
account=schedule.account,
date=service_date,
status='scheduled',
notes=notes,
deadline_start=deadline_start,
deadline_end=deadline_end
)
# Add admin profile to team_members by default
admin_profile_id = "7dc00b89-72d1-4dea-a1ed-4cbef220aa0c"
try:
admin_profile = Profile.objects.get(id=admin_profile_id)
service.team_members.add(admin_profile)
except Profile.DoesNotExist:
# If admin profile doesn't exist, continue without adding it
pass
return service
@classmethod
def generate_services_for_month(cls, schedule: Schedule, year: int, month: int) -> Dict[str, Any]:
"""
Generate services for a specific month based on a schedule.
Args:
schedule: The schedule to base the services on
year: The year
month: The month (1-12)
Returns:
Dict[str, Any]: Dictionary with generated services and any errors
"""
if schedule.schedule_exception:
return {
'services': [],
'success': False,
'errors': [f"Schedule exception: {schedule.schedule_exception}"],
'message': "Schedule has an exception and requires manual scheduling"
}
# Get the start and end dates of the month
month_start = date(year, month, 1)
month_end = get_month_start_end(datetime(year, month, 1))[1].date()
services = []
errors = []
# Iterate through each day in the month
current_date = month_start
while current_date <= month_end:
weekday = current_date.weekday() # Monday is 0, Sunday is 6
day_name = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'][weekday]
# Check if service is required for this day
is_service_day = schedule.has_service_on_day(day_name)
is_weekend = weekday >= 5 and schedule.weekend_service
# If it's a service day or a weekend with weekend_service enabled
if is_service_day or is_weekend:
# TODO: Check if it's a holiday (would need a holiday model/service)
# For now, we'll assume it's not a holiday
is_holiday = False
if not is_holiday:
try:
# For weekend services, use the actual date but mark as weekend service
service = cls.create_service(schedule, current_date, is_weekend=is_weekend)
# Serialize the service object to make it JSON serializable
serialized_service = cls.serialize_service(service)
services.append(serialized_service)
except Exception as e:
errors.append(f"Error creating service for {current_date}: {str(e)}")
current_date += timedelta(days=1)
return {
'services': services,
'success': len(services) > 0,
'errors': errors,
'message': f"Generated {len(services)} services for {month}/{year}"
}
@classmethod
def generate_services_for_accounts(cls, schedules: List[Schedule], year: int, month: int) -> Dict[str, Any]:
"""
Generate services for multiple accounts based on their schedules.
Args:
schedules: List of schedules to generate services for
year: The year
month: The month (1-12)
Returns:
Dict[str, Any]: Dictionary with results for each account
"""
results = {}
overall_success = True
overall_errors = []
for schedule in schedules:
account_id = str(schedule.account.id)
account_name = schedule.account.name
# Skip inactive schedules
if not schedule.is_active:
results[account_id] = {
'account_name': account_name,
'services': [],
'success': False,
'errors': ["Schedule is inactive"],
'message': "Schedule is inactive"
}
continue
# Generate services for this account
account_result = cls.generate_services_for_month(schedule, year, month)
# Add account name to the result
account_result['account_name'] = account_name
# Store the result
results[account_id] = account_result
# Update overall success and errors
if not account_result['success']:
overall_success = False
if account_result['errors']:
for error in account_result['errors']:
overall_errors.append(f"{account_name}: {error}")
return {
'account_results': results,
'success': overall_success,
'errors': overall_errors,
'message': f"Generated services for {len(results)} accounts for {month}/{year}"
}