mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 13:35:19 -05:00
Compare commits
6 Commits
95700c7d7b
...
nuxt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c6e219662 | ||
|
|
70e4385c2b | ||
|
|
30aa887d2a | ||
|
|
dd2d09b1c7 | ||
|
|
89d9e945b9 | ||
|
|
bc4a3c7557 |
@@ -26,6 +26,7 @@ from django.utils.crypto import get_random_string
|
|||||||
from django_forwardemail.services import EmailService
|
from django_forwardemail.services import EmailService
|
||||||
|
|
||||||
from .models import EmailVerification, User, UserDeletionRequest, UserProfile
|
from .models import EmailVerification, User, UserDeletionRequest, UserProfile
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ class AccountService:
|
|||||||
html=email_html,
|
html=email_html,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send password change confirmation email: {e}")
|
capture_and_log(e, 'Send password change confirmation email', source='service', severity='medium')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def initiate_email_change(
|
def initiate_email_change(
|
||||||
@@ -206,7 +207,7 @@ class AccountService:
|
|||||||
html=email_html,
|
html=email_html,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send email verification: {e}")
|
capture_and_log(e, 'Send email verification', source='service', severity='medium')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def verify_email_change(*, token: str) -> dict[str, Any]:
|
def verify_email_change(*, token: str) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from django.utils import timezone
|
|||||||
from django_forwardemail.services import EmailService
|
from django_forwardemail.services import EmailService
|
||||||
|
|
||||||
from apps.accounts.models import NotificationPreference, User, UserNotification
|
from apps.accounts.models import NotificationPreference, User, UserNotification
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -264,7 +265,7 @@ class NotificationService:
|
|||||||
logger.info(f"Email notification sent to {user.email} for notification {notification.id}")
|
logger.info(f"Email notification sent to {user.email} for notification {notification.id}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send email notification {notification.id}: {str(e)}")
|
capture_and_log(e, f'Send email notification {notification.id}', source='service')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_notifications(
|
def get_user_notifications(
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ if TYPE_CHECKING:
|
|||||||
else:
|
else:
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +64,7 @@ class SocialProviderService:
|
|||||||
return True, "Provider can be safely disconnected."
|
return True, "Provider can be safely disconnected."
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error checking disconnect permission for user {user.id}, provider {provider}: {e}")
|
capture_and_log(e, f'Check disconnect permission for user {user.id}, provider {provider}', source='service')
|
||||||
return False, "Unable to verify disconnection safety. Please try again."
|
return False, "Unable to verify disconnection safety. Please try again."
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -97,7 +99,7 @@ class SocialProviderService:
|
|||||||
return connected_providers
|
return connected_providers
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting connected providers for user {user.id}: {e}")
|
capture_and_log(e, f'Get connected providers for user {user.id}', source='service')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -140,7 +142,7 @@ class SocialProviderService:
|
|||||||
return available_providers
|
return available_providers
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting available providers: {e}")
|
capture_and_log(e, 'Get available providers', source='service')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -177,7 +179,7 @@ class SocialProviderService:
|
|||||||
return True, f"{provider.title()} account disconnected successfully."
|
return True, f"{provider.title()} account disconnected successfully."
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error disconnecting {provider} for user {user.id}: {e}")
|
capture_and_log(e, f'Disconnect {provider} for user {user.id}', source='service')
|
||||||
return False, f"Failed to disconnect {provider} account. Please try again."
|
return False, f"Failed to disconnect {provider} account. Please try again."
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -210,7 +212,7 @@ class SocialProviderService:
|
|||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting auth status for user {user.id}: {e}")
|
capture_and_log(e, f'Get auth status for user {user.id}', source='service')
|
||||||
return {"error": "Unable to retrieve authentication status"}
|
return {"error": "Unable to retrieve authentication status"}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -236,5 +238,5 @@ class SocialProviderService:
|
|||||||
return True, f"Provider '{provider}' is valid and available."
|
return True, f"Provider '{provider}' is valid and available."
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error validating provider {provider}: {e}")
|
capture_and_log(e, f'Validate provider {provider}', source='service')
|
||||||
return False, "Unable to validate provider."
|
return False, "Unable to validate provider."
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ from django.db import transaction
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
@@ -292,5 +294,5 @@ class UserDeletionService:
|
|||||||
logger.info(f"Deletion verification email sent to {user.email}")
|
logger.info(f"Deletion verification email sent to {user.email}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send deletion verification email to {user.email}: {str(e)}")
|
capture_and_log(e, f'Send deletion verification email to {user.email}', source='service')
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class ParkPhotoOutputSerializer(serializers.ModelSerializer):
|
|||||||
def get_image_url(self, obj):
|
def get_image_url(self, obj):
|
||||||
"""Get the full Cloudflare Images URL."""
|
"""Get the full Cloudflare Images URL."""
|
||||||
if obj.image:
|
if obj.image:
|
||||||
return obj.image.url
|
return obj.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@extend_schema_field(
|
@extend_schema_field(
|
||||||
@@ -95,10 +95,10 @@ class ParkPhotoOutputSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
# Common variants for park photos
|
# Common variants for park photos
|
||||||
variants = {
|
variants = {
|
||||||
"thumbnail": f"{obj.image.url}/thumbnail",
|
"thumbnail": f"{obj.image.public_url}/thumbnail",
|
||||||
"medium": f"{obj.image.url}/medium",
|
"medium": f"{obj.image.public_url}/medium",
|
||||||
"large": f"{obj.image.url}/large",
|
"large": f"{obj.image.public_url}/large",
|
||||||
"public": f"{obj.image.url}/public",
|
"public": f"{obj.image.public_url}/public",
|
||||||
}
|
}
|
||||||
return variants
|
return variants
|
||||||
|
|
||||||
@@ -303,14 +303,14 @@ class HybridParkSerializer(serializers.ModelSerializer):
|
|||||||
def get_banner_image_url(self, obj):
|
def get_banner_image_url(self, obj):
|
||||||
"""Get banner image URL."""
|
"""Get banner image URL."""
|
||||||
if obj.banner_image and obj.banner_image.image:
|
if obj.banner_image and obj.banner_image.image:
|
||||||
return obj.banner_image.image.url
|
return obj.banner_image.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@extend_schema_field(serializers.URLField(allow_null=True))
|
@extend_schema_field(serializers.URLField(allow_null=True))
|
||||||
def get_card_image_url(self, obj):
|
def get_card_image_url(self, obj):
|
||||||
"""Get card image URL."""
|
"""Get card image URL."""
|
||||||
if obj.card_image and obj.card_image.image:
|
if obj.card_image and obj.card_image.image:
|
||||||
return obj.card_image.image.url
|
return obj.card_image.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@extend_schema_field(serializers.BooleanField())
|
@extend_schema_field(serializers.BooleanField())
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class RidePhotoOutputSerializer(serializers.ModelSerializer):
|
|||||||
def get_image_url(self, obj):
|
def get_image_url(self, obj):
|
||||||
"""Get the full Cloudflare Images URL."""
|
"""Get the full Cloudflare Images URL."""
|
||||||
if obj.image:
|
if obj.image:
|
||||||
return obj.image.url
|
return obj.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@extend_schema_field(
|
@extend_schema_field(
|
||||||
@@ -97,10 +97,10 @@ class RidePhotoOutputSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
# Common variants for ride photos
|
# Common variants for ride photos
|
||||||
variants = {
|
variants = {
|
||||||
"thumbnail": f"{obj.image.url}/thumbnail",
|
"thumbnail": f"{obj.image.public_url}/thumbnail",
|
||||||
"medium": f"{obj.image.url}/medium",
|
"medium": f"{obj.image.public_url}/medium",
|
||||||
"large": f"{obj.image.url}/large",
|
"large": f"{obj.image.public_url}/large",
|
||||||
"public": f"{obj.image.url}/public",
|
"public": f"{obj.image.public_url}/public",
|
||||||
}
|
}
|
||||||
return variants
|
return variants
|
||||||
|
|
||||||
@@ -481,14 +481,14 @@ class HybridRideSerializer(serializers.ModelSerializer):
|
|||||||
def get_banner_image_url(self, obj):
|
def get_banner_image_url(self, obj):
|
||||||
"""Get banner image URL."""
|
"""Get banner image URL."""
|
||||||
if obj.banner_image and obj.banner_image.image:
|
if obj.banner_image and obj.banner_image.image:
|
||||||
return obj.banner_image.image.url
|
return obj.banner_image.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@extend_schema_field(serializers.URLField(allow_null=True))
|
@extend_schema_field(serializers.URLField(allow_null=True))
|
||||||
def get_card_image_url(self, obj):
|
def get_card_image_url(self, obj):
|
||||||
"""Get card image URL."""
|
"""Get card image URL."""
|
||||||
if obj.card_image and obj.card_image.image:
|
if obj.card_image and obj.card_image.image:
|
||||||
return obj.card_image.image.url
|
return obj.card_image.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Computed property
|
# Computed property
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class RideModelPhotoOutputSerializer(serializers.Serializer):
|
|||||||
def get_image_url(self, obj):
|
def get_image_url(self, obj):
|
||||||
"""Get the image URL."""
|
"""Get the image URL."""
|
||||||
if obj.image:
|
if obj.image:
|
||||||
return obj.image.url
|
return obj.image.public_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -265,13 +265,13 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"id": photo.id,
|
"id": photo.id,
|
||||||
"image_url": photo.image.url if photo.image else None,
|
"image_url": photo.image.public_url if photo.image else None,
|
||||||
"image_variants": (
|
"image_variants": (
|
||||||
{
|
{
|
||||||
"thumbnail": (f"{photo.image.url}/thumbnail" if photo.image else None),
|
"thumbnail": (f"{photo.image.public_url}/thumbnail" if photo.image else None),
|
||||||
"medium": f"{photo.image.url}/medium" if photo.image else None,
|
"medium": f"{photo.image.public_url}/medium" if photo.image else None,
|
||||||
"large": f"{photo.image.url}/large" if photo.image else None,
|
"large": f"{photo.image.public_url}/large" if photo.image else None,
|
||||||
"public": f"{photo.image.url}/public" if photo.image else None,
|
"public": f"{photo.image.public_url}/public" if photo.image else None,
|
||||||
}
|
}
|
||||||
if photo.image
|
if photo.image
|
||||||
else {}
|
else {}
|
||||||
@@ -295,12 +295,12 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
if photo and photo.image:
|
if photo and photo.image:
|
||||||
return {
|
return {
|
||||||
"id": photo.id,
|
"id": photo.id,
|
||||||
"image_url": photo.image.url,
|
"image_url": photo.image.public_url,
|
||||||
"image_variants": {
|
"image_variants": {
|
||||||
"thumbnail": f"{photo.image.url}/thumbnail",
|
"thumbnail": f"{photo.image.public_url}/thumbnail",
|
||||||
"medium": f"{photo.image.url}/medium",
|
"medium": f"{photo.image.public_url}/medium",
|
||||||
"large": f"{photo.image.url}/large",
|
"large": f"{photo.image.public_url}/large",
|
||||||
"public": f"{photo.image.url}/public",
|
"public": f"{photo.image.public_url}/public",
|
||||||
},
|
},
|
||||||
"caption": photo.caption,
|
"caption": photo.caption,
|
||||||
"alt_text": photo.alt_text,
|
"alt_text": photo.alt_text,
|
||||||
@@ -318,12 +318,12 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
if obj.banner_image and obj.banner_image.image:
|
if obj.banner_image and obj.banner_image.image:
|
||||||
return {
|
return {
|
||||||
"id": obj.banner_image.id,
|
"id": obj.banner_image.id,
|
||||||
"image_url": obj.banner_image.image.url,
|
"image_url": obj.banner_image.image.public_url,
|
||||||
"image_variants": {
|
"image_variants": {
|
||||||
"thumbnail": f"{obj.banner_image.image.url}/thumbnail",
|
"thumbnail": f"{obj.banner_image.image.public_url}/thumbnail",
|
||||||
"medium": f"{obj.banner_image.image.url}/medium",
|
"medium": f"{obj.banner_image.image.public_url}/medium",
|
||||||
"large": f"{obj.banner_image.image.url}/large",
|
"large": f"{obj.banner_image.image.public_url}/large",
|
||||||
"public": f"{obj.banner_image.image.url}/public",
|
"public": f"{obj.banner_image.image.public_url}/public",
|
||||||
},
|
},
|
||||||
"caption": obj.banner_image.caption,
|
"caption": obj.banner_image.caption,
|
||||||
"alt_text": obj.banner_image.alt_text,
|
"alt_text": obj.banner_image.alt_text,
|
||||||
@@ -343,12 +343,12 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
if latest_photo and latest_photo.image:
|
if latest_photo and latest_photo.image:
|
||||||
return {
|
return {
|
||||||
"id": latest_photo.id,
|
"id": latest_photo.id,
|
||||||
"image_url": latest_photo.image.url,
|
"image_url": latest_photo.image.public_url,
|
||||||
"image_variants": {
|
"image_variants": {
|
||||||
"thumbnail": f"{latest_photo.image.url}/thumbnail",
|
"thumbnail": f"{latest_photo.image.public_url}/thumbnail",
|
||||||
"medium": f"{latest_photo.image.url}/medium",
|
"medium": f"{latest_photo.image.public_url}/medium",
|
||||||
"large": f"{latest_photo.image.url}/large",
|
"large": f"{latest_photo.image.public_url}/large",
|
||||||
"public": f"{latest_photo.image.url}/public",
|
"public": f"{latest_photo.image.public_url}/public",
|
||||||
},
|
},
|
||||||
"caption": latest_photo.caption,
|
"caption": latest_photo.caption,
|
||||||
"alt_text": latest_photo.alt_text,
|
"alt_text": latest_photo.alt_text,
|
||||||
@@ -367,12 +367,12 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
if obj.card_image and obj.card_image.image:
|
if obj.card_image and obj.card_image.image:
|
||||||
return {
|
return {
|
||||||
"id": obj.card_image.id,
|
"id": obj.card_image.id,
|
||||||
"image_url": obj.card_image.image.url,
|
"image_url": obj.card_image.image.public_url,
|
||||||
"image_variants": {
|
"image_variants": {
|
||||||
"thumbnail": f"{obj.card_image.image.url}/thumbnail",
|
"thumbnail": f"{obj.card_image.image.public_url}/thumbnail",
|
||||||
"medium": f"{obj.card_image.image.url}/medium",
|
"medium": f"{obj.card_image.image.public_url}/medium",
|
||||||
"large": f"{obj.card_image.image.url}/large",
|
"large": f"{obj.card_image.image.public_url}/large",
|
||||||
"public": f"{obj.card_image.image.url}/public",
|
"public": f"{obj.card_image.image.public_url}/public",
|
||||||
},
|
},
|
||||||
"caption": obj.card_image.caption,
|
"caption": obj.card_image.caption,
|
||||||
"alt_text": obj.card_image.alt_text,
|
"alt_text": obj.card_image.alt_text,
|
||||||
@@ -392,12 +392,12 @@ class RideDetailOutputSerializer(serializers.Serializer):
|
|||||||
if latest_photo and latest_photo.image:
|
if latest_photo and latest_photo.image:
|
||||||
return {
|
return {
|
||||||
"id": latest_photo.id,
|
"id": latest_photo.id,
|
||||||
"image_url": latest_photo.image.url,
|
"image_url": latest_photo.image.public_url,
|
||||||
"image_variants": {
|
"image_variants": {
|
||||||
"thumbnail": f"{latest_photo.image.url}/thumbnail",
|
"thumbnail": f"{latest_photo.image.public_url}/thumbnail",
|
||||||
"medium": f"{latest_photo.image.url}/medium",
|
"medium": f"{latest_photo.image.public_url}/medium",
|
||||||
"large": f"{latest_photo.image.url}/large",
|
"large": f"{latest_photo.image.public_url}/large",
|
||||||
"public": f"{latest_photo.image.url}/public",
|
"public": f"{latest_photo.image.public_url}/public",
|
||||||
},
|
},
|
||||||
"caption": latest_photo.caption,
|
"caption": latest_photo.caption,
|
||||||
"alt_text": latest_photo.alt_text,
|
"alt_text": latest_photo.alt_text,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from django.utils import timezone
|
|||||||
|
|
||||||
from apps.parks.models import Park
|
from apps.parks.models import Park
|
||||||
from apps.rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(f" {item['name']} ({item['park']}) - opened: {item['date_opened']}")
|
self.stdout.write(f" {item['name']} ({item['park']}) - opened: {item['date_opened']}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error calculating new content: {e}", exc_info=True)
|
capture_and_log(e, 'Calculate new content', source='management', severity='high')
|
||||||
raise CommandError(f"Failed to calculate new content: {e}") from None
|
raise CommandError(f"Failed to calculate new content: {e}") from None
|
||||||
|
|
||||||
def _get_new_parks(self, cutoff_date: datetime, limit: int) -> list[dict[str, Any]]:
|
def _get_new_parks(self, cutoff_date: datetime, limit: int) -> list[dict[str, Any]]:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from django.utils import timezone
|
|||||||
from apps.core.analytics import PageView
|
from apps.core.analytics import PageView
|
||||||
from apps.parks.models import Park
|
from apps.parks.models import Park
|
||||||
from apps.rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(f" {item['name']} (score: {item.get('views_change', 'N/A')})")
|
self.stdout.write(f" {item['name']} (score: {item.get('views_change', 'N/A')})")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error calculating trending content: {e}", exc_info=True)
|
capture_and_log(e, 'Calculate trending content', source='management', severity='high')
|
||||||
raise CommandError(f"Failed to calculate trending content: {e}") from None
|
raise CommandError(f"Failed to calculate trending content: {e}") from None
|
||||||
|
|
||||||
def _calculate_trending_parks(
|
def _calculate_trending_parks(
|
||||||
@@ -199,7 +200,7 @@ class Command(BaseCommand):
|
|||||||
return final_score
|
return final_score
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error calculating score for {content_type} {content_obj.id}: {e}")
|
capture_and_log(e, f'Calculate score for {content_type} {content_obj.id}', source='management', severity='medium')
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
def _calculate_view_growth_score(
|
def _calculate_view_growth_score(
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from django.conf import settings
|
|||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
performance_logger = logging.getLogger("performance")
|
performance_logger = logging.getLogger("performance")
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -130,12 +132,11 @@ class PerformanceMiddleware(MiddlewareMixin):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
performance_logger.error(
|
capture_and_log(
|
||||||
f"Request exception: {request.method} {request.path} - "
|
exception,
|
||||||
f"{duration:.3f}s, {total_queries} queries, {type(exception).__name__}: {
|
f'Request exception: {request.method} {request.path} - {duration:.3f}s, {total_queries} queries',
|
||||||
exception
|
source='middleware',
|
||||||
}",
|
severity='high',
|
||||||
extra=performance_data,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Don't return anything - let the exception propagate normally
|
# Don't return anything - let the exception propagate normally
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ from collections.abc import Callable
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||||
|
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -215,7 +217,9 @@ class SecurityEventLogger:
|
|||||||
user = getattr(request, "user", None)
|
user = getattr(request, "user", None)
|
||||||
username = user.username if user and user.is_authenticated else "anonymous"
|
username = user.username if user and user.is_authenticated else "anonymous"
|
||||||
|
|
||||||
logger.error(
|
capture_and_log(
|
||||||
f"Suspicious activity detected - Type: {activity_type}, "
|
RuntimeError(f'Suspicious activity detected - Type: {activity_type}'),
|
||||||
f"IP: {client_ip}, User: {username}, Details: {details}"
|
f'Suspicious activity - IP: {client_ip}, User: {username}, Details: {details}',
|
||||||
|
source='security',
|
||||||
|
severity='high',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -130,23 +130,28 @@ class ErrorService:
|
|||||||
# Merge request_context into metadata
|
# Merge request_context into metadata
|
||||||
merged_metadata = {**(metadata or {}), "request_context": request_context}
|
merged_metadata = {**(metadata or {}), "request_context": request_context}
|
||||||
|
|
||||||
|
# Build create kwargs, only including error_id if provided
|
||||||
|
create_kwargs = {
|
||||||
|
"error_type": error_type,
|
||||||
|
"error_message": error_message[:5000], # Limit message length
|
||||||
|
"error_stack": error_stack[:10000], # Limit stack length
|
||||||
|
"error_code": error_code,
|
||||||
|
"severity": severity,
|
||||||
|
"source": source,
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"http_method": http_method,
|
||||||
|
"user_agent": user_agent[:1000],
|
||||||
|
"user": user,
|
||||||
|
"ip_address_hash": ip_address_hash,
|
||||||
|
"metadata": merged_metadata,
|
||||||
|
"environment": environment or {},
|
||||||
|
}
|
||||||
|
# Only include error_id if explicitly provided, else let model default
|
||||||
|
if error_id is not None:
|
||||||
|
create_kwargs["error_id"] = error_id
|
||||||
|
|
||||||
# Create and save error
|
# Create and save error
|
||||||
app_error = ApplicationError.objects.create(
|
app_error = ApplicationError.objects.create(**create_kwargs)
|
||||||
error_id=error_id or None, # Let model generate if not provided
|
|
||||||
error_type=error_type,
|
|
||||||
error_message=error_message[:5000], # Limit message length
|
|
||||||
error_stack=error_stack[:10000], # Limit stack length
|
|
||||||
error_code=error_code,
|
|
||||||
severity=severity,
|
|
||||||
source=source,
|
|
||||||
endpoint=endpoint,
|
|
||||||
http_method=http_method,
|
|
||||||
user_agent=user_agent[:1000],
|
|
||||||
user=user,
|
|
||||||
ip_address_hash=ip_address_hash,
|
|
||||||
metadata=merged_metadata,
|
|
||||||
environment=environment or {},
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Captured error {app_error.short_error_id}: {error_type} from {source}"
|
f"Captured error {app_error.short_error_id}: {error_type} from {source}"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ from django.conf import settings
|
|||||||
from django.core.files.uploadedfile import UploadedFile
|
from django.core.files.uploadedfile import UploadedFile
|
||||||
from PIL import ExifTags, Image
|
from PIL import ExifTags, Image
|
||||||
|
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -193,5 +195,5 @@ class MediaService:
|
|||||||
"available_space": "unknown",
|
"available_space": "unknown",
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to get storage stats: {str(e)}")
|
capture_and_log(e, 'Get storage stats', source='service', severity='low')
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ class TrendingService:
|
|||||||
# Get card image URL
|
# Get card image URL
|
||||||
card_image_url = ""
|
card_image_url = ""
|
||||||
if park.card_image and hasattr(park.card_image, "image"):
|
if park.card_image and hasattr(park.card_image, "image"):
|
||||||
card_image_url = park.card_image.image.url if park.card_image.image else ""
|
card_image_url = park.card_image.image.public_url if park.card_image.image else ""
|
||||||
|
|
||||||
# Get primary company (operator)
|
# Get primary company (operator)
|
||||||
primary_company = park.operator.name if park.operator else ""
|
primary_company = park.operator.name if park.operator else ""
|
||||||
@@ -247,7 +247,7 @@ class TrendingService:
|
|||||||
# Get card image URL
|
# Get card image URL
|
||||||
card_image_url = ""
|
card_image_url = ""
|
||||||
if ride.card_image and hasattr(ride.card_image, "image"):
|
if ride.card_image and hasattr(ride.card_image, "image"):
|
||||||
card_image_url = ride.card_image.image.url if ride.card_image.image else ""
|
card_image_url = ride.card_image.image.public_url if ride.card_image.image else ""
|
||||||
|
|
||||||
trending_rides.append(
|
trending_rides.append(
|
||||||
{
|
{
|
||||||
@@ -450,7 +450,7 @@ class TrendingService:
|
|||||||
# Get card image URL
|
# Get card image URL
|
||||||
card_image_url = ""
|
card_image_url = ""
|
||||||
if park.card_image and hasattr(park.card_image, "image"):
|
if park.card_image and hasattr(park.card_image, "image"):
|
||||||
card_image_url = park.card_image.image.url if park.card_image.image else ""
|
card_image_url = park.card_image.image.public_url if park.card_image.image else ""
|
||||||
|
|
||||||
# Get primary company (operator)
|
# Get primary company (operator)
|
||||||
primary_company = park.operator.name if park.operator else ""
|
primary_company = park.operator.name if park.operator else ""
|
||||||
@@ -506,7 +506,7 @@ class TrendingService:
|
|||||||
# Get card image URL
|
# Get card image URL
|
||||||
card_image_url = ""
|
card_image_url = ""
|
||||||
if ride.card_image and hasattr(ride.card_image, "image"):
|
if ride.card_image and hasattr(ride.card_image, "image"):
|
||||||
card_image_url = ride.card_image.image.url if ride.card_image.image else ""
|
card_image_url = ride.card_image.image.public_url if ride.card_image.image else ""
|
||||||
|
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -496,9 +496,10 @@ class TransitionCallbackRegistry:
|
|||||||
failures.append((callback, None))
|
failures.append((callback, None))
|
||||||
overall_success = False
|
overall_success = False
|
||||||
|
|
||||||
if not callback.continue_on_error:
|
if not callback.continue_on_error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Aborting callback chain - {callback.name} failed " f"and continue_on_error=False"
|
f"Aborting callback chain - {callback.name} failed "
|
||||||
|
f"and continue_on_error=False"
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -509,7 +510,8 @@ class TransitionCallbackRegistry:
|
|||||||
|
|
||||||
if not callback.continue_on_error:
|
if not callback.continue_on_error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Aborting callback chain - {callback.name} raised exception " f"and continue_on_error=False"
|
f"Aborting callback chain - {callback.name} raised exception "
|
||||||
|
f"and continue_on_error=False"
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ def with_callbacks(
|
|||||||
if not pre_success and pre_failures:
|
if not pre_success and pre_failures:
|
||||||
for callback, exc in pre_failures:
|
for callback, exc in pre_failures:
|
||||||
if not callback.continue_on_error:
|
if not callback.continue_on_error:
|
||||||
logger.error(f"Pre-transition callback {callback.name} failed, " f"aborting transition")
|
logger.error(f"Pre-transition callback {callback.name} failed, "
|
||||||
|
f"aborting transition")
|
||||||
if exc:
|
if exc:
|
||||||
raise exc
|
raise exc
|
||||||
raise RuntimeError(f"Pre-transition callback {callback.name} failed")
|
raise RuntimeError(f"Pre-transition callback {callback.name} failed")
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ def get_og_image(
|
|||||||
try:
|
try:
|
||||||
first_photo = instance.photos.first()
|
first_photo = instance.photos.first()
|
||||||
if first_photo and hasattr(first_photo, "image"):
|
if first_photo and hasattr(first_photo, "image"):
|
||||||
return urljoin(base_url, first_photo.image.url)
|
return urljoin(base_url, first_photo.image.public_url)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from apps.rides.models import (
|
|||||||
RollerCoasterStats,
|
RollerCoasterStats,
|
||||||
)
|
)
|
||||||
from apps.rides.models.company import Company as RideCompany
|
from apps.rides.models.company import Company as RideCompany
|
||||||
|
from apps.core.utils import capture_and_log
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -111,9 +112,11 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(self.style.SUCCESS("Successfully cleaned up existing sample data!"))
|
self.stdout.write(self.style.SUCCESS("Successfully cleaned up existing sample data!"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
capture_and_log(
|
||||||
f"Error during data cleanup: {str(e)}",
|
e,
|
||||||
exc_info=True,
|
'Data cleanup error',
|
||||||
|
source='management',
|
||||||
|
severity='high',
|
||||||
)
|
)
|
||||||
self.stdout.write(self.style.ERROR(f"Failed to clean up existing data: {str(e)}"))
|
self.stdout.write(self.style.ERROR(f"Failed to clean up existing data: {str(e)}"))
|
||||||
raise
|
raise
|
||||||
@@ -152,7 +155,7 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(self.style.SUCCESS("Successfully created comprehensive sample data!"))
|
self.stdout.write(self.style.SUCCESS("Successfully created comprehensive sample data!"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error during sample data creation: {str(e)}", exc_info=True)
|
capture_and_log(e, 'Sample data creation error', source='management', severity='high')
|
||||||
self.stdout.write(self.style.ERROR(f"Failed to create sample data: {str(e)}"))
|
self.stdout.write(self.style.ERROR(f"Failed to create sample data: {str(e)}"))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -333,7 +336,7 @@ class Command(BaseCommand):
|
|||||||
}"
|
}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating park company {data['name']}: {str(e)}")
|
capture_and_log(e, f"Create park company {data['name']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Create companies in rides app (for manufacturers and designers)
|
# Create companies in rides app (for manufacturers and designers)
|
||||||
@@ -356,11 +359,11 @@ class Command(BaseCommand):
|
|||||||
}"
|
}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating ride company {data['name']}: {str(e)}")
|
capture_and_log(e, f"Create ride company {data['name']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in create_companies: {str(e)}")
|
capture_and_log(e, 'Create companies', source='management', severity='high')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_parks(self):
|
def create_parks(self):
|
||||||
@@ -518,19 +521,18 @@ class Command(BaseCommand):
|
|||||||
park_location.set_coordinates(loc_data["latitude"], loc_data["longitude"])
|
park_location.set_coordinates(loc_data["latitude"], loc_data["longitude"])
|
||||||
park_location.save()
|
park_location.save()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
capture_and_log(
|
||||||
f"Error creating location for park {
|
e, f"Create location for park {park_data['name']}",
|
||||||
park_data['name']
|
source='management', severity='medium'
|
||||||
}: {str(e)}"
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating park {park_data['name']}: {str(e)}")
|
capture_and_log(e, f"Create park {park_data['name']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in create_parks: {str(e)}")
|
capture_and_log(e, 'Create parks', source='management', severity='high')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_rides(self):
|
def create_rides(self):
|
||||||
@@ -597,7 +599,7 @@ class Command(BaseCommand):
|
|||||||
}"
|
}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating ride model {model_data['name']}: {str(e)}")
|
capture_and_log(e, f"Create ride model {model_data['name']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Create rides
|
# Create rides
|
||||||
@@ -822,19 +824,18 @@ class Command(BaseCommand):
|
|||||||
stats_data = ride_data["coaster_stats"]
|
stats_data = ride_data["coaster_stats"]
|
||||||
RollerCoasterStats.objects.create(ride=ride, **stats_data)
|
RollerCoasterStats.objects.create(ride=ride, **stats_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
capture_and_log(
|
||||||
f"Error creating stats for ride {ride_data['name']}: {
|
e, f"Create stats for ride {ride_data['name']}",
|
||||||
str(e)
|
source='management', severity='medium'
|
||||||
}"
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating ride {ride_data['name']}: {str(e)}")
|
capture_and_log(e, f"Create ride {ride_data['name']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in create_rides: {str(e)}")
|
capture_and_log(e, 'Create rides', source='management', severity='high')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_park_areas(self):
|
def create_park_areas(self):
|
||||||
@@ -967,11 +968,11 @@ class Command(BaseCommand):
|
|||||||
} in {park.name}"
|
} in {park.name}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error creating areas for park {area_group['park']}: {str(e)}")
|
capture_and_log(e, f"Create areas for park {area_group['park']}", source='management', severity='medium')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in create_park_areas: {str(e)}")
|
capture_and_log(e, 'Create park areas', source='management', severity='high')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_reviews(self):
|
def create_reviews(self):
|
||||||
@@ -1043,10 +1044,9 @@ class Command(BaseCommand):
|
|||||||
}"
|
}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
capture_and_log(
|
||||||
f"Error creating park review for {review_data['park']}: {
|
e, f"Create park review for {review_data['park']}",
|
||||||
str(e)
|
source='management', severity='medium'
|
||||||
}"
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -1102,15 +1102,14 @@ class Command(BaseCommand):
|
|||||||
}"
|
}"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
capture_and_log(
|
||||||
f"Error creating ride review for {review_data['ride']}: {
|
e, f"Create ride review for {review_data['ride']}",
|
||||||
str(e)
|
source='management', severity='medium'
|
||||||
}"
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS("Sample data creation completed!"))
|
self.stdout.write(self.style.SUCCESS("Sample data creation completed!"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in create_reviews: {str(e)}")
|
capture_and_log(e, 'Create reviews', source='management', severity='high')
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -382,6 +382,7 @@ class Park(StateMachineMixin, TrackedModel):
|
|||||||
# Try pghistory events
|
# Try pghistory events
|
||||||
print("Searching pghistory events")
|
print("Searching pghistory events")
|
||||||
event_model = getattr(cls, "event_model", None)
|
event_model = getattr(cls, "event_model", None)
|
||||||
|
historical_event = None
|
||||||
if event_model:
|
if event_model:
|
||||||
historical_event = event_model.objects.filter(slug=slug).order_by("-pgh_created_at").first()
|
historical_event = event_model.objects.filter(slug=slug).order_by("-pgh_created_at").first()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user