Files
thrillwiki_django_no_react/backend/apps/rides/services.py
pacnpal 7ba0004c93 chore: fix pghistory migration deps and improve htmx utilities
- Update pghistory dependency from 0007 to 0006 in account migrations
- Add docstrings and remove unused imports in htmx_forms.py
- Add DJANGO_SETTINGS_MODULE bash commands to Claude settings
- Add state transition definitions for ride statuses
2025-12-21 17:33:24 -05:00

310 lines
8.6 KiB
Python

"""
Services for ride-related business logic.
Following Django styleguide pattern for business logic encapsulation.
"""
from typing import Optional, Dict, Any
from django.db import transaction
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser
from apps.rides.models import Ride
# Use AbstractBaseUser for type hinting
UserType = AbstractBaseUser
User = get_user_model()
class RideService:
"""Service for managing ride operations."""
@staticmethod
def create_ride(
*,
name: str,
park_id: int,
description: str = "",
status: str = "OPERATING",
category: str = "",
manufacturer_id: Optional[int] = None,
designer_id: Optional[int] = None,
ride_model_id: Optional[int] = None,
park_area_id: Optional[int] = None,
opening_date: Optional[str] = None,
closing_date: Optional[str] = None,
created_by: Optional[UserType] = None,
) -> Ride:
"""
Create a new ride with validation.
Args:
name: Ride name
park_id: ID of the park
description: Ride description
status: Operating status
category: Ride category
manufacturer_id: ID of manufacturer company
designer_id: ID of designer company
ride_model_id: ID of ride model
park_area_id: ID of park area
opening_date: Opening date
closing_date: Closing date
created_by: User creating the ride
Returns:
Created Ride instance
Raises:
ValidationError: If ride data is invalid
"""
with transaction.atomic():
from apps.parks.models import Park
# Get park
park = Park.objects.get(id=park_id)
# Create ride instance
ride = Ride(
name=name,
park=park,
description=description,
status=status,
category=category,
opening_date=opening_date,
closing_date=closing_date,
)
# Set foreign key relationships if provided
if park_area_id:
from apps.parks.models import ParkArea
ride.park_area = ParkArea.objects.get(id=park_area_id)
if manufacturer_id:
from apps.rides.models import Company
ride.manufacturer = Company.objects.get(id=manufacturer_id)
if designer_id:
from apps.rides.models import Company
ride.designer = Company.objects.get(id=designer_id)
if ride_model_id:
from apps.rides.models import RideModel
ride.ride_model = RideModel.objects.get(id=ride_model_id)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
ride.full_clean()
ride.save()
return ride
@staticmethod
def update_ride(
*,
ride_id: int,
updates: Dict[str, Any],
updated_by: Optional[UserType] = None,
) -> Ride:
"""
Update an existing ride with validation.
Args:
ride_id: ID of ride to update
updates: Dictionary of field updates
updated_by: User performing the update
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
ValidationError: If update data is invalid
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
# Apply updates
for field, value in updates.items():
if hasattr(ride, field):
setattr(ride, field, value)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
ride.full_clean()
ride.save()
return ride
@staticmethod
def close_ride_temporarily(
*, ride_id: int, user: Optional[UserType] = None
) -> Ride:
"""
Temporarily close a ride.
Args:
ride_id: ID of ride to close temporarily
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.close_temporarily(user=user)
return ride
@staticmethod
def mark_ride_sbno(
*, ride_id: int, user: Optional[UserType] = None
) -> Ride:
"""
Mark a ride as SBNO (Standing But Not Operating).
Args:
ride_id: ID of ride to mark as SBNO
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.mark_sbno(user=user)
return ride
@staticmethod
def schedule_ride_closing(
*,
ride_id: int,
closing_date,
post_closing_status: str,
user: Optional[UserType] = None,
) -> Ride:
"""
Schedule a ride to close on a specific date with a post-closing status.
Args:
ride_id: ID of ride to schedule for closing
closing_date: Date when ride will close
post_closing_status: Status to transition to after closing
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
ValidationError: If post_closing_status is not set
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.mark_closing(
closing_date=closing_date,
post_closing_status=post_closing_status,
user=user,
)
return ride
@staticmethod
def close_ride_permanently(
*, ride_id: int, user: Optional[UserType] = None
) -> Ride:
"""
Permanently close a ride.
Args:
ride_id: ID of ride to close permanently
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.close_permanently(user=user)
return ride
@staticmethod
def demolish_ride(*, ride_id: int, user: Optional[UserType] = None) -> Ride:
"""
Mark a ride as demolished.
Args:
ride_id: ID of ride to demolish
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.demolish(user=user)
return ride
@staticmethod
def relocate_ride(
*, ride_id: int, new_park_id: int, user: Optional[UserType] = None
) -> Ride:
"""
Relocate a ride to a new park.
Args:
ride_id: ID of ride to relocate
new_park_id: ID of the new park
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
from apps.parks.models import Park
ride = Ride.objects.select_for_update().get(id=ride_id)
new_park = Park.objects.get(id=new_park_id)
# Mark as relocated first
ride.relocate(user=user)
# Move to new park
ride.move_to_park(new_park, clear_park_area=True)
return ride
@staticmethod
def reopen_ride(*, ride_id: int, user: Optional[UserType] = None) -> Ride:
"""
Reopen a ride for operation.
Args:
ride_id: ID of ride to reopen
user: User performing the action
Returns:
Updated Ride instance
Raises:
Ride.DoesNotExist: If ride doesn't exist
"""
with transaction.atomic():
ride = Ride.objects.select_for_update().get(id=ride_id)
ride.open(user=user)
return ride