Refactor test utilities and enhance ASGI settings

- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -3,10 +3,9 @@ Services for park-related business logic.
Following Django styleguide pattern for business logic encapsulation.
"""
from typing import Optional, Dict, Any, Tuple
from typing import Optional, Dict, Any
from django.db import transaction
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser
@@ -20,7 +19,7 @@ User = get_user_model()
class ParkService:
"""Service for managing park operations."""
@staticmethod
def create_park(
*,
@@ -35,11 +34,11 @@ class ParkService:
size_acres: Optional[float] = None,
website: str = "",
location_data: Optional[Dict[str, Any]] = None,
created_by: Optional[UserType] = None
created_by: Optional[UserType] = None,
) -> Park:
"""
Create a new park with validation and location handling.
Args:
name: Park name
description: Park description
@@ -53,10 +52,10 @@ class ParkService:
website: Park website URL
location_data: Dictionary containing location information
created_by: User creating the park
Returns:
Created Park instance
Raises:
ValidationError: If park data is invalid
"""
@@ -70,175 +69,169 @@ class ParkService:
closing_date=closing_date,
operating_season=operating_season,
size_acres=size_acres,
website=website
website=website,
)
# Set foreign key relationships if provided
if operator_id:
from .models import Company
park.operator = Company.objects.get(id=operator_id)
if property_owner_id:
from .models import Company
park.property_owner = Company.objects.get(id=property_owner_id)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
park.full_clean()
park.save()
# Handle location if provided
if location_data:
LocationService.create_park_location(
park=park,
**location_data
)
LocationService.create_park_location(park=park, **location_data)
return park
@staticmethod
def update_park(
*,
park_id: int,
updates: Dict[str, Any],
updated_by: Optional[UserType] = None
updated_by: Optional[UserType] = None,
) -> Park:
"""
Update an existing park with validation.
Args:
park_id: ID of park to update
updates: Dictionary of field updates
updated_by: User performing the update
Returns:
Updated Park instance
Raises:
Park.DoesNotExist: If park doesn't exist
ValidationError: If update data is invalid
"""
with transaction.atomic():
park = Park.objects.select_for_update().get(id=park_id)
# Apply updates
for field, value in updates.items():
if hasattr(park, field):
setattr(park, field, value)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
park.full_clean()
park.save()
return park
@staticmethod
def delete_park(*, park_id: int, deleted_by: Optional[UserType] = None) -> bool:
"""
Soft delete a park by setting status to DEMOLISHED.
Args:
park_id: ID of park to delete
deleted_by: User performing the deletion
Returns:
True if successfully deleted
Raises:
Park.DoesNotExist: If park doesn't exist
"""
with transaction.atomic():
park = Park.objects.select_for_update().get(id=park_id)
park.status = 'DEMOLISHED'
park.status = "DEMOLISHED"
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
park.full_clean()
park.save()
return True
@staticmethod
def create_park_area(
*,
park_id: int,
name: str,
description: str = "",
created_by: Optional[UserType] = None
created_by: Optional[UserType] = None,
) -> ParkArea:
"""
Create a new area within a park.
Args:
park_id: ID of the parent park
name: Area name
description: Area description
created_by: User creating the area
Returns:
Created ParkArea instance
Raises:
Park.DoesNotExist: If park doesn't exist
ValidationError: If area data is invalid
"""
park = Park.objects.get(id=park_id)
area = ParkArea(
park=park,
name=name,
description=description
)
area = ParkArea(park=park, name=name, description=description)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
area.full_clean()
area.save()
return area
@staticmethod
def update_park_statistics(*, park_id: int) -> Park:
"""
Recalculate and update park statistics (ride counts, ratings).
Args:
park_id: ID of park to update statistics for
Returns:
Updated Park instance with fresh statistics
"""
from rides.models import Ride
from .models import ParkReview
from django.db.models import Count, Avg
with transaction.atomic():
park = Park.objects.select_for_update().get(id=park_id)
# Calculate ride counts
ride_stats = Ride.objects.filter(park=park).aggregate(
total_rides=Count('id'),
coaster_count=Count('id', filter=Q(category__in=['RC', 'WC']))
total_rides=Count("id"),
coaster_count=Count("id", filter=Q(category__in=["RC", "WC"])),
)
# Calculate average rating
avg_rating = ParkReview.objects.filter(
park=park,
is_published=True
).aggregate(avg_rating=Avg('rating'))['avg_rating']
park=park, is_published=True
).aggregate(avg_rating=Avg("rating"))["avg_rating"]
# Update park fields
park.ride_count = ride_stats['total_rides'] or 0
park.coaster_count = ride_stats['coaster_count'] or 0
park.ride_count = ride_stats["total_rides"] or 0
park.coaster_count = ride_stats["coaster_count"] or 0
park.average_rating = avg_rating
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
park.full_clean()
park.save()
return park
class LocationService:
"""Service for managing location operations."""
@staticmethod
def create_park_location(
*,
@@ -249,11 +242,11 @@ class LocationService:
city: str = "",
state: str = "",
country: str = "",
postal_code: str = ""
postal_code: str = "",
) -> Location:
"""
Create a location for a park.
Args:
park: Park instance
latitude: Latitude coordinate
@@ -263,71 +256,68 @@ class LocationService:
state: State/region name
country: Country name
postal_code: Postal/ZIP code
Returns:
Created Location instance
Raises:
ValidationError: If location data is invalid
"""
location = Location(
content_object=park,
name=park.name,
location_type='park',
location_type="park",
latitude=latitude,
longitude=longitude,
street_address=street_address,
city=city,
state=state,
country=country,
postal_code=postal_code
postal_code=postal_code,
)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
location.full_clean()
location.save()
return location
@staticmethod
def update_park_location(
*,
park_id: int,
location_updates: Dict[str, Any]
*, park_id: int, location_updates: Dict[str, Any]
) -> Location:
"""
Update location information for a park.
Args:
park_id: ID of the park
location_updates: Dictionary of location field updates
Returns:
Updated Location instance
Raises:
Location.DoesNotExist: If location doesn't exist
ValidationError: If location data is invalid
"""
with transaction.atomic():
park = Park.objects.get(id=park_id)
try:
location = park.location
except Location.DoesNotExist:
# Create location if it doesn't exist
return LocationService.create_park_location(
park=park,
**location_updates
park=park, **location_updates
)
# Apply updates
for field, value in location_updates.items():
if hasattr(location, field):
setattr(location, field, value)
# CRITICAL STYLEGUIDE FIX: Call full_clean before save
location.full_clean()
location.save()
return location