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

@@ -7,6 +7,7 @@ from django.contrib.gis.geos import Point
import pghistory
from core.history import TrackedModel
@pghistory.track()
class Location(TrackedModel):
"""
@@ -14,84 +15,93 @@ class Location(TrackedModel):
using GenericForeignKey. Stores detailed location information
including coordinates and address components.
"""
# Generic relation fields
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
content_object = GenericForeignKey("content_type", "object_id")
# Location name and type
name = models.CharField(max_length=255, help_text="Name of the location (e.g. business name, landmark)")
location_type = models.CharField(max_length=50, help_text="Type of location (e.g. business, landmark, address)")
name = models.CharField(
max_length=255,
help_text="Name of the location (e.g. business name, landmark)",
)
location_type = models.CharField(
max_length=50,
help_text="Type of location (e.g. business, landmark, address)",
)
# Geographic coordinates
latitude = models.DecimalField(
max_digits=9,
max_digits=9,
decimal_places=6,
validators=[
MinValueValidator(-90),
MaxValueValidator(90)
],
validators=[MinValueValidator(-90), MaxValueValidator(90)],
help_text="Latitude coordinate (legacy field)",
null=True,
blank=True
blank=True,
)
longitude = models.DecimalField(
max_digits=9,
max_digits=9,
decimal_places=6,
validators=[
MinValueValidator(-180),
MaxValueValidator(180)
],
validators=[MinValueValidator(-180), MaxValueValidator(180)],
help_text="Longitude coordinate (legacy field)",
null=True,
blank=True
blank=True,
)
# GeoDjango point field
point = gis_models.PointField(
srid=4326, # WGS84 coordinate system
null=True,
blank=True,
help_text="Geographic coordinates as a Point"
help_text="Geographic coordinates as a Point",
)
# Address components
street_address = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(max_length=100, blank=True, null=True, help_text="State/Region/Province")
state = models.CharField(
max_length=100,
blank=True,
null=True,
help_text="State/Region/Province",
)
country = models.CharField(max_length=100, blank=True, null=True)
postal_code = models.CharField(max_length=20, blank=True, null=True)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
indexes = [
models.Index(fields=['content_type', 'object_id']),
models.Index(fields=['city']),
models.Index(fields=['country']),
models.Index(fields=["content_type", "object_id"]),
models.Index(fields=["city"]),
models.Index(fields=["country"]),
]
ordering = ['name']
ordering = ["name"]
constraints = [
# Business rule: Latitude must be within valid range (-90 to 90)
models.CheckConstraint(
name="location_latitude_range",
check=models.Q(latitude__isnull=True) | (models.Q(latitude__gte=-90) & models.Q(latitude__lte=90)),
violation_error_message="Latitude must be between -90 and 90 degrees"
check=models.Q(latitude__isnull=True)
| (models.Q(latitude__gte=-90) & models.Q(latitude__lte=90)),
violation_error_message="Latitude must be between -90 and 90 degrees",
),
# Business rule: Longitude must be within valid range (-180 to 180)
models.CheckConstraint(
name="location_longitude_range",
check=models.Q(longitude__isnull=True) | (models.Q(longitude__gte=-180) & models.Q(longitude__lte=180)),
violation_error_message="Longitude must be between -180 and 180 degrees"
check=models.Q(longitude__isnull=True)
| (models.Q(longitude__gte=-180) & models.Q(longitude__lte=180)),
violation_error_message="Longitude must be between -180 and 180 degrees",
),
# Business rule: If coordinates are provided, both lat and lng must be present
# Business rule: If coordinates are provided, both lat and lng must
# be present
models.CheckConstraint(
name="location_coordinates_complete",
check=models.Q(latitude__isnull=True, longitude__isnull=True) |
models.Q(latitude__isnull=False, longitude__isnull=False),
violation_error_message="Both latitude and longitude must be provided together"
check=models.Q(latitude__isnull=True, longitude__isnull=True)
| models.Q(latitude__isnull=False, longitude__isnull=False),
violation_error_message="Both latitude and longitude must be provided together",
),
]
@@ -101,7 +111,9 @@ class Location(TrackedModel):
location_parts.append(self.city)
if self.country:
location_parts.append(self.country)
location_str = ", ".join(location_parts) if location_parts else "Unknown location"
location_str = (
", ".join(location_parts) if location_parts else "Unknown location"
)
return f"{self.name} ({location_str})"
def save(self, *args, **kwargs):
@@ -132,7 +144,8 @@ class Location(TrackedModel):
def coordinates(self):
"""Returns coordinates as a tuple"""
if self.point:
return (self.point.y, self.point.x) # Returns (latitude, longitude)
# Returns (latitude, longitude)
return (self.point.y, self.point.x)
elif self.latitude is not None and self.longitude is not None:
return (float(self.latitude), float(self.longitude))
return None
@@ -153,7 +166,10 @@ class Location(TrackedModel):
"""
if not self.point:
return Location.objects.none()
return Location.objects.filter(
point__distance_lte=(self.point, distance_km * 1000) # Convert km to meters
point__distance_lte=(
self.point,
distance_km * 1000,
) # Convert km to meters
).exclude(pk=self.pk)