""" Services for ride status transitions and management. Following Django styleguide pattern for business logic encapsulation. """ from django.contrib.auth.models import AbstractBaseUser from django.db import transaction from apps.rides.models import Ride class RideStatusService: """Service for managing ride status transitions using FSM.""" @staticmethod def open_ride(*, ride_id: int, user: AbstractBaseUser | None = None) -> Ride: """ Open a ride for operation. Args: ride_id: ID of ride to open 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 @staticmethod def close_ride_temporarily( *, ride_id: int, user: AbstractBaseUser | None = 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: AbstractBaseUser | None = 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 mark_ride_closing( *, ride_id: int, closing_date, post_closing_status: str, user: AbstractBaseUser | None = None, ) -> Ride: """ Mark a ride as closing with a specific date and post-closing status. Args: ride_id: ID of ride to mark as 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: AbstractBaseUser | None = 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: AbstractBaseUser | None = 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, user: AbstractBaseUser | None = None) -> Ride: """ Mark a ride as relocated. Args: ride_id: ID of ride to relocate 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.relocate(user=user) return ride @staticmethod def process_closing_rides() -> list[Ride]: """ Process all rides in CLOSING status and transition them to their post_closing_status if the closing_date has been reached. Returns: List of rides that were transitioned Note: This method should be called by a scheduled task/cron job. """ from django.utils import timezone transitioned_rides = [] closing_rides = Ride.objects.filter( status="CLOSING", closing_date__lte=timezone.now().date(), ).select_for_update() for ride in closing_rides: try: with transaction.atomic(): ride.apply_post_closing_status() transitioned_rides.append(ride) except Exception as e: # Log error but continue processing other rides import logging logger = logging.getLogger(__name__) logger.error( f"Failed to process closing ride {ride.id}: {e}", exc_info=True, ) continue return transitioned_rides