okay fine

This commit is contained in:
pacnpal
2024-11-03 17:47:26 +00:00
parent 387c4740e7
commit 27f3326e22
10020 changed files with 1935769 additions and 2364 deletions

View File

@@ -1,8 +1,55 @@
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
from django.urls import reverse
from django.utils.text import slugify
from django.contrib.contenttypes.fields import GenericRelation
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError
from decimal import Decimal, ROUND_DOWN, InvalidOperation
from simple_history.models import HistoricalRecords
from cities_light.models import Country, Region, City
from companies.models import Company
from media.models import Photo
def normalize_coordinate(value, max_digits, decimal_places):
"""Normalize coordinate to have at most max_digits total digits and decimal_places decimal places"""
try:
if value is None:
return None
# Convert to Decimal for precise handling
value = Decimal(str(value))
# Round to specified decimal places
value = Decimal(value.quantize(Decimal('0.' + '0' * decimal_places), rounding=ROUND_DOWN))
return value
except (TypeError, ValueError, InvalidOperation):
return None
def validate_coordinate_digits(value, max_digits, decimal_places):
"""Validate total number of digits in a coordinate value"""
if value is not None:
try:
# Convert to Decimal for precise handling
value = Decimal(str(value))
# Round to exactly 6 decimal places
value = value.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
return value
except (InvalidOperation, TypeError):
raise ValidationError('Invalid coordinate value.')
return value
def validate_latitude_digits(value):
"""Validate total number of digits in latitude"""
return validate_coordinate_digits(value, 9, 6)
def validate_longitude_digits(value):
"""Validate total number of digits in longitude"""
return validate_coordinate_digits(value, 10, 6)
class Park(models.Model):
STATUS_CHOICES = [
@@ -16,23 +63,45 @@ class Park(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
location = models.CharField(max_length=255, blank=True, null=True) # Made nullable
country = models.ForeignKey(Country, on_delete=models.PROTECT)
region = models.ForeignKey(Region, on_delete=models.PROTECT, null=True, blank=True)
city = models.ForeignKey(City, on_delete=models.PROTECT, null=True, blank=True)
description = models.TextField(blank=True)
owner = models.ForeignKey(
'companies.Company',
on_delete=models.SET_NULL,
related_name='parks',
null=True,
blank=True
)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='OPERATING'
)
# Location fields
latitude = models.DecimalField(
max_digits=9,
decimal_places=6,
null=True,
blank=True,
help_text='Latitude coordinate (-90 to 90)',
validators=[
MinValueValidator(Decimal('-90')),
MaxValueValidator(Decimal('90')),
validate_latitude_digits,
]
)
longitude = models.DecimalField(
max_digits=10,
decimal_places=6,
null=True,
blank=True,
help_text='Longitude coordinate (-180 to 180)',
validators=[
MinValueValidator(Decimal('-180')),
MaxValueValidator(Decimal('180')),
validate_longitude_digits,
]
)
street_address = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=255, blank=True)
state = models.CharField(max_length=255, blank=True)
country = models.CharField(max_length=255, blank=True)
postal_code = models.CharField(max_length=20, blank=True)
# Details
opening_date = models.DateField(null=True, blank=True)
closing_date = models.DateField(null=True, blank=True)
operating_season = models.CharField(max_length=255, blank=True)
@@ -43,16 +112,30 @@ class Park(models.Model):
blank=True
)
website = models.URLField(blank=True)
# Statistics
average_rating = models.DecimalField(
max_digits=3,
decimal_places=2,
null=True,
blank=True
)
total_rides = models.IntegerField(null=True, blank=True)
total_roller_coasters = models.IntegerField(null=True, blank=True)
# Relationships
owner = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='parks'
)
photos = GenericRelation(Photo, related_query_name='park')
# Metadata
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
photos = GenericRelation('media.Photo')
reviews = GenericRelation('reviews.Review')
history = HistoricalRecords()
class Meta:
@@ -65,11 +148,17 @@ class Park(models.Model):
if not self.slug:
self.slug = slugify(self.name)
# Update the location field to combine country, region, and city
self.location = self.get_formatted_location()
# Normalize coordinates before saving
if self.latitude is not None:
self.latitude = normalize_coordinate(self.latitude, 9, 6)
if self.longitude is not None:
self.longitude = normalize_coordinate(self.longitude, 10, 6)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('parks:park_detail', kwargs={'slug': self.slug})
@classmethod
def get_by_slug(cls, slug):
"""Get park by current or historical slug"""
@@ -79,37 +168,28 @@ class Park(models.Model):
# Check historical slugs
history = cls.history.filter(slug=slug).order_by('-history_date').first()
if history:
return cls.objects.get(id=history.id), True
raise cls.DoesNotExist("No park found with this slug")
try:
return cls.objects.get(id=history.id), True
except cls.DoesNotExist:
pass
raise cls.DoesNotExist()
def get_formatted_location(self):
"""Get a formatted location string: $COUNTRY, $REGION, $CITY"""
if not self.country:
return ""
location = self.country.name
if self.region and self.city:
location += f", {self.region.name}, {self.city.name}"
elif self.region:
location += f", {self.region.name}"
return location
class ParkArea(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
description = models.TextField(blank=True)
park = models.ForeignKey(
Park,
on_delete=models.CASCADE,
related_name='areas'
)
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
description = models.TextField(blank=True)
opening_date = models.DateField(null=True, blank=True)
closing_date = models.DateField(null=True, blank=True)
# Metadata
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
photos = GenericRelation('media.Photo')
history = HistoricalRecords()
class Meta:
@@ -117,13 +197,19 @@ class ParkArea(models.Model):
unique_together = ['park', 'slug']
def __str__(self):
return f"{self.park.name} - {self.name}"
return f"{self.name} at {self.park.name}"
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('parks:area_detail', kwargs={
'park_slug': self.park.slug,
'area_slug': self.slug
})
@classmethod
def get_by_slug(cls, slug):
"""Get area by current or historical slug"""
@@ -133,5 +219,8 @@ class ParkArea(models.Model):
# Check historical slugs
history = cls.history.filter(slug=slug).order_by('-history_date').first()
if history:
return cls.objects.get(id=history.id), True
raise cls.DoesNotExist("No area found with this slug")
try:
return cls.objects.get(id=history.id), True
except cls.DoesNotExist:
pass
raise cls.DoesNotExist()