203 lines
7.4 KiB
Python
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}"
|
|
}
|