fixed a bunch of things, hopefully didn't break things

This commit is contained in:
pacnpal
2024-11-05 21:51:02 +00:00
parent 2e8a725933
commit eb5d2acab5
30 changed files with 944 additions and 569 deletions

View File

@@ -1,83 +1,90 @@
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
from django.utils.text import slugify
from simple_history.models import HistoricalRecords
from django.urls import reverse
from typing import Tuple, Optional, ClassVar, TYPE_CHECKING
if TYPE_CHECKING:
from history_tracking.models import HistoricalSlug
class Company(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
website = models.URLField(blank=True)
headquarters = models.CharField(max_length=255, blank=True)
description = models.TextField(blank=True)
website = models.URLField(blank=True)
founded_date = models.DateField(null=True, blank=True)
total_parks = models.IntegerField(default=0)
total_rides = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
photos = GenericRelation('media.Photo')
history = HistoricalRecords()
# Stats fields
total_parks = models.PositiveIntegerField(default=0)
total_rides = models.PositiveIntegerField(default=0)
objects: ClassVar[models.Manager['Company']]
class Meta:
verbose_name_plural = "companies"
verbose_name_plural = 'companies'
ordering = ['name']
def __str__(self):
def __str__(self) -> str:
return self.name
def save(self, *args, **kwargs):
def save(self, *args, **kwargs) -> None:
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
@classmethod
def get_by_slug(cls, slug):
"""Get company by current or historical slug"""
def get_by_slug(cls, slug: str) -> Tuple['Company', bool]:
"""Get company by slug, checking historical slugs if needed"""
try:
return cls.objects.get(slug=slug), False
except cls.DoesNotExist:
# 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 company found with this slug")
from history_tracking.models import HistoricalSlug
try:
historical = HistoricalSlug.objects.get(
content_type__model='company',
slug=slug
)
return cls.objects.get(pk=historical.object_id), True
except (HistoricalSlug.DoesNotExist, cls.DoesNotExist):
raise cls.DoesNotExist()
class Manufacturer(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
website = models.URLField(blank=True)
headquarters = models.CharField(max_length=255, blank=True)
description = models.TextField(blank=True)
website = models.URLField(blank=True)
founded_date = models.DateField(null=True, blank=True)
total_rides = models.IntegerField(default=0)
total_roller_coasters = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
photos = GenericRelation('media.Photo')
history = HistoricalRecords()
# Stats fields
total_rides = models.PositiveIntegerField(default=0)
total_roller_coasters = models.PositiveIntegerField(default=0)
objects: ClassVar[models.Manager['Manufacturer']]
class Meta:
ordering = ['name']
def __str__(self):
def __str__(self) -> str:
return self.name
def save(self, *args, **kwargs):
def save(self, *args, **kwargs) -> None:
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
@classmethod
def get_by_slug(cls, slug):
"""Get manufacturer by current or historical slug"""
def get_by_slug(cls, slug: str) -> Tuple['Manufacturer', bool]:
"""Get manufacturer by slug, checking historical slugs if needed"""
try:
return cls.objects.get(slug=slug), False
except cls.DoesNotExist:
# 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 manufacturer found with this slug")
from history_tracking.models import HistoricalSlug
try:
historical = HistoricalSlug.objects.get(
content_type__model='manufacturer',
slug=slug
)
return cls.objects.get(pk=historical.object_id), True
except (HistoricalSlug.DoesNotExist, cls.DoesNotExist):
raise cls.DoesNotExist()

View File

@@ -5,6 +5,8 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.geos import Point
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http import HttpResponse
from typing import cast, Tuple, Optional
from .models import Company, Manufacturer
from location.models import Location
from moderation.models import EditSubmission, PhotoSubmission
@@ -13,7 +15,7 @@ from media.models import Photo
User = get_user_model()
class CompanyModelTests(TestCase):
def setUp(self):
def setUp(self) -> None:
self.company = Company.objects.create(
name='Test Company',
website='http://example.com',
@@ -36,7 +38,7 @@ class CompanyModelTests(TestCase):
point=Point(-118.2437, 34.0522)
)
def test_company_creation(self):
def test_company_creation(self) -> None:
"""Test company instance creation and field values"""
self.assertEqual(self.company.name, 'Test Company')
self.assertEqual(self.company.website, 'http://example.com')
@@ -46,22 +48,22 @@ class CompanyModelTests(TestCase):
self.assertEqual(self.company.total_rides, 100)
self.assertTrue(self.company.slug)
def test_company_str_representation(self):
def test_company_str_representation(self) -> None:
"""Test string representation of company"""
self.assertEqual(str(self.company), 'Test Company')
def test_company_get_by_slug(self):
def test_company_get_by_slug(self) -> None:
"""Test get_by_slug class method"""
company, is_historical = Company.get_by_slug(self.company.slug)
self.assertEqual(company, self.company)
self.assertFalse(is_historical)
def test_company_get_by_invalid_slug(self):
def test_company_get_by_invalid_slug(self) -> None:
"""Test get_by_slug with invalid slug"""
with self.assertRaises(Company.DoesNotExist):
Company.get_by_slug('invalid-slug')
def test_company_stats(self):
def test_company_stats(self) -> None:
"""Test company statistics fields"""
self.company.total_parks = 10
self.company.total_rides = 200
@@ -72,7 +74,7 @@ class CompanyModelTests(TestCase):
self.assertEqual(company.total_rides, 200)
class ManufacturerModelTests(TestCase):
def setUp(self):
def setUp(self) -> None:
self.manufacturer = Manufacturer.objects.create(
name='Test Manufacturer',
website='http://example.com',
@@ -95,7 +97,7 @@ class ManufacturerModelTests(TestCase):
point=Point(-118.2437, 34.0522)
)
def test_manufacturer_creation(self):
def test_manufacturer_creation(self) -> None:
"""Test manufacturer instance creation and field values"""
self.assertEqual(self.manufacturer.name, 'Test Manufacturer')
self.assertEqual(self.manufacturer.website, 'http://example.com')
@@ -105,22 +107,22 @@ class ManufacturerModelTests(TestCase):
self.assertEqual(self.manufacturer.total_roller_coasters, 20)
self.assertTrue(self.manufacturer.slug)
def test_manufacturer_str_representation(self):
def test_manufacturer_str_representation(self) -> None:
"""Test string representation of manufacturer"""
self.assertEqual(str(self.manufacturer), 'Test Manufacturer')
def test_manufacturer_get_by_slug(self):
def test_manufacturer_get_by_slug(self) -> None:
"""Test get_by_slug class method"""
manufacturer, is_historical = Manufacturer.get_by_slug(self.manufacturer.slug)
self.assertEqual(manufacturer, self.manufacturer)
self.assertFalse(is_historical)
def test_manufacturer_get_by_invalid_slug(self):
def test_manufacturer_get_by_invalid_slug(self) -> None:
"""Test get_by_slug with invalid slug"""
with self.assertRaises(Manufacturer.DoesNotExist):
Manufacturer.get_by_slug('invalid-slug')
def test_manufacturer_stats(self):
def test_manufacturer_stats(self) -> None:
"""Test manufacturer statistics fields"""
self.manufacturer.total_rides = 100
self.manufacturer.total_roller_coasters = 40
@@ -131,7 +133,7 @@ class ManufacturerModelTests(TestCase):
self.assertEqual(manufacturer.total_roller_coasters, 40)
class CompanyViewTests(TestCase):
def setUp(self):
def setUp(self) -> None:
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
@@ -164,13 +166,13 @@ class CompanyViewTests(TestCase):
point=Point(-118.2437, 34.0522)
)
def test_company_list_view(self):
def test_company_list_view(self) -> None:
"""Test company list view"""
response = self.client.get(reverse('companies:company_list'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, self.company.name)
def test_company_list_view_with_search(self):
def test_company_list_view_with_search(self) -> None:
"""Test company list view with search"""
response = self.client.get(reverse('companies:company_list') + '?search=Test')
self.assertEqual(response.status_code, 200)
@@ -180,7 +182,7 @@ class CompanyViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, self.company.name)
def test_company_list_view_with_country_filter(self):
def test_company_list_view_with_country_filter(self) -> None:
"""Test company list view with country filter"""
response = self.client.get(reverse('companies:company_list') + '?country=Test Country')
self.assertEqual(response.status_code, 200)
@@ -190,7 +192,7 @@ class CompanyViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, self.company.name)
def test_company_detail_view(self):
def test_company_detail_view(self) -> None:
"""Test company detail view"""
response = self.client.get(
reverse('companies:company_detail', kwargs={'slug': self.company.slug})
@@ -200,25 +202,25 @@ class CompanyViewTests(TestCase):
self.assertContains(response, self.company.website)
self.assertContains(response, self.company.headquarters)
def test_company_detail_view_invalid_slug(self):
def test_company_detail_view_invalid_slug(self) -> None:
"""Test company detail view with invalid slug"""
response = self.client.get(
reverse('companies:company_detail', kwargs={'slug': 'invalid-slug'})
)
self.assertEqual(response.status_code, 404)
def test_company_create_view_unauthenticated(self):
def test_company_create_view_unauthenticated(self) -> None:
"""Test company create view when not logged in"""
response = self.client.get(reverse('companies:company_create'))
self.assertEqual(response.status_code, 302) # Redirects to login
def test_company_create_view_authenticated(self):
def test_company_create_view_authenticated(self) -> None:
"""Test company create view when logged in"""
self.client.login(username='testuser', password='testpass123')
response = self.client.get(reverse('companies:company_create'))
self.assertEqual(response.status_code, 200)
def test_company_create_submission_regular_user(self):
def test_company_create_submission_regular_user(self) -> None:
"""Test creating a company submission as regular user"""
self.client.login(username='testuser', password='testpass123')
data = {
@@ -237,7 +239,7 @@ class CompanyViewTests(TestCase):
status='NEW'
).exists())
def test_company_create_submission_moderator(self):
def test_company_create_submission_moderator(self) -> None:
"""Test creating a company submission as moderator"""
self.client.login(username='moderator', password='modpass123')
data = {
@@ -257,7 +259,7 @@ class CompanyViewTests(TestCase):
self.assertEqual(submission.status, 'APPROVED')
self.assertEqual(submission.handled_by, self.moderator)
def test_company_photo_submission(self):
def test_company_photo_submission(self) -> None:
"""Test photo submission for company"""
self.client.login(username='testuser', password='testpass123')
image_content = b'GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;'
@@ -267,19 +269,19 @@ class CompanyViewTests(TestCase):
'caption': 'Test Photo',
'date_taken': '2024-01-01'
}
response = self.client.post(
response = cast(HttpResponse, self.client.post(
reverse('companies:company_detail', kwargs={'slug': self.company.slug}),
data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest' # Simulate AJAX request
)
))
self.assertEqual(response.status_code, 200)
self.assertTrue(PhotoSubmission.objects.filter(
content_type=ContentType.objects.get_for_model(Company),
object_id=self.company.id
object_id=self.company.pk
).exists())
class ManufacturerViewTests(TestCase):
def setUp(self):
def setUp(self) -> None:
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
@@ -312,13 +314,13 @@ class ManufacturerViewTests(TestCase):
point=Point(-118.2437, 34.0522)
)
def test_manufacturer_list_view(self):
def test_manufacturer_list_view(self) -> None:
"""Test manufacturer list view"""
response = self.client.get(reverse('companies:manufacturer_list'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, self.manufacturer.name)
def test_manufacturer_list_view_with_search(self):
def test_manufacturer_list_view_with_search(self) -> None:
"""Test manufacturer list view with search"""
response = self.client.get(reverse('companies:manufacturer_list') + '?search=Test')
self.assertEqual(response.status_code, 200)
@@ -328,7 +330,7 @@ class ManufacturerViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, self.manufacturer.name)
def test_manufacturer_list_view_with_country_filter(self):
def test_manufacturer_list_view_with_country_filter(self) -> None:
"""Test manufacturer list view with country filter"""
response = self.client.get(reverse('companies:manufacturer_list') + '?country=Test Country')
self.assertEqual(response.status_code, 200)
@@ -338,7 +340,7 @@ class ManufacturerViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, self.manufacturer.name)
def test_manufacturer_detail_view(self):
def test_manufacturer_detail_view(self) -> None:
"""Test manufacturer detail view"""
response = self.client.get(
reverse('companies:manufacturer_detail', kwargs={'slug': self.manufacturer.slug})
@@ -348,25 +350,25 @@ class ManufacturerViewTests(TestCase):
self.assertContains(response, self.manufacturer.website)
self.assertContains(response, self.manufacturer.headquarters)
def test_manufacturer_detail_view_invalid_slug(self):
def test_manufacturer_detail_view_invalid_slug(self) -> None:
"""Test manufacturer detail view with invalid slug"""
response = self.client.get(
reverse('companies:manufacturer_detail', kwargs={'slug': 'invalid-slug'})
)
self.assertEqual(response.status_code, 404)
def test_manufacturer_create_view_unauthenticated(self):
def test_manufacturer_create_view_unauthenticated(self) -> None:
"""Test manufacturer create view when not logged in"""
response = self.client.get(reverse('companies:manufacturer_create'))
self.assertEqual(response.status_code, 302) # Redirects to login
def test_manufacturer_create_view_authenticated(self):
def test_manufacturer_create_view_authenticated(self) -> None:
"""Test manufacturer create view when logged in"""
self.client.login(username='testuser', password='testpass123')
response = self.client.get(reverse('companies:manufacturer_create'))
self.assertEqual(response.status_code, 200)
def test_manufacturer_create_submission_regular_user(self):
def test_manufacturer_create_submission_regular_user(self) -> None:
"""Test creating a manufacturer submission as regular user"""
self.client.login(username='testuser', password='testpass123')
data = {
@@ -385,7 +387,7 @@ class ManufacturerViewTests(TestCase):
status='NEW'
).exists())
def test_manufacturer_create_submission_moderator(self):
def test_manufacturer_create_submission_moderator(self) -> None:
"""Test creating a manufacturer submission as moderator"""
self.client.login(username='moderator', password='modpass123')
data = {
@@ -405,7 +407,7 @@ class ManufacturerViewTests(TestCase):
self.assertEqual(submission.status, 'APPROVED')
self.assertEqual(submission.handled_by, self.moderator)
def test_manufacturer_photo_submission(self):
def test_manufacturer_photo_submission(self) -> None:
"""Test photo submission for manufacturer"""
self.client.login(username='testuser', password='testpass123')
image_content = b'GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;'
@@ -415,13 +417,13 @@ class ManufacturerViewTests(TestCase):
'caption': 'Test Photo',
'date_taken': '2024-01-01'
}
response = self.client.post(
response = cast(HttpResponse, self.client.post(
reverse('companies:manufacturer_detail', kwargs={'slug': self.manufacturer.slug}),
data,
HTTP_X_REQUESTED_WITH='XMLHttpRequest' # Simulate AJAX request
)
))
self.assertEqual(response.status_code, 200)
self.assertTrue(PhotoSubmission.objects.filter(
content_type=ContentType.objects.get_for_model(Manufacturer),
object_id=self.manufacturer.id
object_id=self.manufacturer.pk
).exists())

View File

@@ -1,11 +1,13 @@
from typing import Any, Optional, Tuple, Type, cast, Union, Dict, Callable
from django.views.generic import DetailView, ListView, CreateView, UpdateView
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.contrib import messages
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.db.models import Count, Sum, Q
from django.http import HttpResponseRedirect, Http404, JsonResponse, HttpResponse
from django.db.models import Count, Sum, Q, QuerySet, Model
from django.contrib.auth import get_user_model
from .models import Company, Manufacturer
from .forms import CompanyForm, ManufacturerForm
from rides.models import Ride
@@ -15,302 +17,349 @@ from core.views import SlugRedirectMixin
from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin
from moderation.models import EditSubmission
User = get_user_model()
ModelType = Union[Type[Company], Type[Manufacturer]]
def get_company_parks(company: Company) -> QuerySet[Park]:
"""Get parks owned by a company with related data."""
return Park.objects.filter(
owner=company
).select_related('owner')
def get_company_ride_count(parks: QuerySet[Park]) -> int:
"""Get total number of rides across all parks."""
return Ride.objects.filter(park__in=parks).count()
def get_manufacturer_rides(manufacturer: Manufacturer) -> QuerySet[Ride]:
"""Get rides made by a manufacturer with related data."""
return Ride.objects.filter(
manufacturer=manufacturer
).select_related('park', 'coaster_stats')
def get_manufacturer_stats(rides: QuerySet[Ride]) -> Dict[str, int]:
"""Get statistics for manufacturer rides."""
return {
'coaster_count': rides.filter(category='ROLLER_COASTER').count(),
'parks_count': rides.values('park').distinct().count()
}
def handle_submission_post(
request: Any,
handle_photo_submission: Callable[[Any], HttpResponse],
super_post: Callable[..., HttpResponse],
*args: Any,
**kwargs: Any
) -> HttpResponse:
"""Handle POST requests for photos and edits."""
if request.FILES:
# Handle photo submission
return handle_photo_submission(request)
# Handle edit submission
return super_post(request, *args, **kwargs)
# List Views
class CompanyListView(ListView):
model = Company
template_name = 'companies/company_list.html'
context_object_name = 'companies'
model: Type[Company] = Company
template_name = "companies/company_list.html"
context_object_name = "companies"
paginate_by = 12
def get_queryset(self):
queryset = Company.objects.all()
# Filter by country if specified
country = self.request.GET.get('country')
if country:
def get_queryset(self) -> QuerySet[Company]:
queryset = self.model.objects.all()
if country := self.request.GET.get("country"):
# Get companies that have locations in the specified country
company_ids = Location.objects.filter(
content_type=ContentType.objects.get_for_model(Company),
country__iexact=country
).values_list('object_id', flat=True)
queryset = queryset.filter(id__in=company_ids)
# Search by name if specified
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(name__icontains=search)
return queryset.order_by('name')
country__iexact=country,
).values_list("object_id", flat=True)
queryset = queryset.filter(pk__in=company_ids)
def get_context_data(self, **kwargs):
if search := self.request.GET.get("search"):
queryset = queryset.filter(name__icontains=search)
return queryset.order_by("name")
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
# Add filter values to context
context['country'] = self.request.GET.get('country', '')
context['search'] = self.request.GET.get('search', '')
context["country"] = self.request.GET.get("country", "")
context["search"] = self.request.GET.get("search", "")
return context
class ManufacturerListView(ListView):
model = Manufacturer
template_name = 'companies/manufacturer_list.html'
context_object_name = 'manufacturers'
model: Type[Manufacturer] = Manufacturer
template_name = "companies/manufacturer_list.html"
context_object_name = "manufacturers"
paginate_by = 12
def get_queryset(self):
queryset = Manufacturer.objects.all()
# Filter by country if specified
country = self.request.GET.get('country')
if country:
def get_queryset(self) -> QuerySet[Manufacturer]:
queryset = self.model.objects.all()
if country := self.request.GET.get("country"):
# Get manufacturers that have locations in the specified country
manufacturer_ids = Location.objects.filter(
content_type=ContentType.objects.get_for_model(Manufacturer),
country__iexact=country
).values_list('object_id', flat=True)
queryset = queryset.filter(id__in=manufacturer_ids)
# Search by name if specified
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(name__icontains=search)
return queryset.order_by('name')
country__iexact=country,
).values_list("object_id", flat=True)
queryset = queryset.filter(pk__in=manufacturer_ids)
def get_context_data(self, **kwargs):
if search := self.request.GET.get("search"):
queryset = queryset.filter(name__icontains=search)
return queryset.order_by("name")
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
# Add stats for filtering
context['total_manufacturers'] = self.model.objects.count()
context['total_rides'] = Ride.objects.filter(
manufacturer__isnull=False
).count()
context['total_roller_coasters'] = Ride.objects.filter(
manufacturer__isnull=False,
category='ROLLER_COASTER'
context["total_manufacturers"] = self.model.objects.count()
context["total_rides"] = Ride.objects.filter(manufacturer__isnull=False).count()
context["total_roller_coasters"] = Ride.objects.filter(
manufacturer__isnull=False, category="ROLLER_COASTER"
).count()
# Add filter values to context
context['country'] = self.request.GET.get('country', '')
context['search'] = self.request.GET.get('search', '')
context["country"] = self.request.GET.get("country", "")
context["search"] = self.request.GET.get("search", "")
return context
# Detail Views
class CompanyDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin, DetailView):
model = Company
model: Type[Company] = Company
template_name = 'companies/company_detail.html'
context_object_name = 'company'
def get_object(self, queryset=None):
def get_object(self, queryset: Optional[QuerySet[Company]] = None) -> Company:
if queryset is None:
queryset = self.get_queryset()
slug = self.kwargs.get(self.slug_url_kwarg)
try:
# Try to get by current or historical slug
return self.model.get_by_slug(slug)[0]
except self.model.DoesNotExist:
raise Http404(f"No {self.model._meta.verbose_name} found matching the query")
model = cast(Type[Company], self.model)
obj, _ = model.get_by_slug(slug)
return obj
except model.DoesNotExist as e:
raise Http404(f"No {model._meta.verbose_name} found matching the query") from e
def get_context_data(self, **kwargs):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
parks = Park.objects.filter(
owner=self.object
).select_related('owner')
company = cast(Company, self.object)
parks = get_company_parks(company)
context['parks'] = parks
context['total_rides'] = Ride.objects.filter(park__in=parks).count()
context['total_rides'] = get_company_ride_count(parks)
return context
def get_redirect_url_pattern(self):
def get_redirect_url_pattern(self) -> str:
return 'companies:company_detail'
def post(self, request, *args, **kwargs):
"""Handle POST requests for photos and edits"""
if request.FILES:
# Handle photo submission
return self.handle_photo_submission(request)
# Handle edit submission
return super().post(request, *args, **kwargs)
def post(self, request: Any, *args: Any, **kwargs: Any) -> HttpResponse:
"""Handle POST requests for photos and edits."""
return handle_submission_post(
request,
self.handle_photo_submission,
super().post,
*args,
**kwargs
)
class ManufacturerDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin, DetailView):
model = Manufacturer
model: Type[Manufacturer] = Manufacturer
template_name = 'companies/manufacturer_detail.html'
context_object_name = 'manufacturer'
def get_object(self, queryset=None):
def get_object(self, queryset: Optional[QuerySet[Manufacturer]] = None) -> Manufacturer:
if queryset is None:
queryset = self.get_queryset()
slug = self.kwargs.get(self.slug_url_kwarg)
try:
# Try to get by current or historical slug
return self.model.get_by_slug(slug)[0]
except self.model.DoesNotExist:
raise Http404(f"No {self.model._meta.verbose_name} found matching the query")
model = cast(Type[Manufacturer], self.model)
obj, _ = model.get_by_slug(slug)
return obj
except model.DoesNotExist as e:
raise Http404(f"No {model._meta.verbose_name} found matching the query") from e
def get_context_data(self, **kwargs):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
rides = Ride.objects.filter(
manufacturer=self.object
).select_related('park', 'coaster_stats')
manufacturer = cast(Manufacturer, self.object)
rides = get_manufacturer_rides(manufacturer)
context['rides'] = rides
context['coaster_count'] = rides.filter(category='ROLLER_COASTER').count()
context['parks_count'] = rides.values('park').distinct().count()
context.update(get_manufacturer_stats(rides))
return context
def get_redirect_url_pattern(self):
def get_redirect_url_pattern(self) -> str:
return 'companies:manufacturer_detail'
def post(self, request, *args, **kwargs):
"""Handle POST requests for photos and edits"""
if request.FILES:
# Handle photo submission
return self.handle_photo_submission(request)
# Handle edit submission
return super().post(request, *args, **kwargs)
def post(self, request: Any, *args: Any, **kwargs: Any) -> HttpResponse:
"""Handle POST requests for photos and edits."""
return handle_submission_post(
request,
self.handle_photo_submission,
super().post,
*args,
**kwargs
)
def _handle_submission(
request: Any, form: Any, model: ModelType, success_url: str
) -> HttpResponseRedirect:
"""Helper method to handle form submissions"""
cleaned_data = form.cleaned_data.copy()
submission = EditSubmission.objects.create(
user=request.user,
content_type=ContentType.objects.get_for_model(model),
submission_type="CREATE",
changes=cleaned_data,
reason=request.POST.get("reason", ""),
source=request.POST.get("source", ""),
)
# Get user role safely
user_role = getattr(request.user, "role", None)
# If user is moderator or above, auto-approve
if user_role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
obj = form.save()
submission.object_id = obj.pk
submission.status = "APPROVED"
submission.handled_by = request.user
submission.save()
messages.success(request, f'Successfully created {getattr(obj, "name", "")}')
return HttpResponseRedirect(success_url)
messages.success(request, "Your submission has been sent for review")
return HttpResponseRedirect(reverse(f"companies:{model.__name__.lower()}_list"))
# Create Views
class CompanyCreateView(LoginRequiredMixin, CreateView):
model = Company
model: Type[Company] = Company
form_class = CompanyForm
template_name = 'companies/company_form.html'
template_name = "companies/company_form.html"
object: Optional[Company]
def form_valid(self, form):
cleaned_data = form.cleaned_data.copy()
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Company),
submission_type='CREATE',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
def form_valid(self, form: CompanyForm) -> HttpResponseRedirect:
success_url = reverse(
"companies:company_detail", kwargs={"slug": form.instance.slug}
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
self.object = form.save()
submission.object_id = self.object.id
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
messages.success(self.request, f'Successfully created {self.object.name}')
return HttpResponseRedirect(self.get_success_url())
messages.success(self.request, 'Your company submission has been sent for review')
return HttpResponseRedirect(reverse('companies:company_list'))
return _handle_submission(self.request, form, self.model, success_url)
def get_success_url(self) -> str:
if self.object is None:
return reverse("companies:company_list")
return reverse("companies:company_detail", kwargs={"slug": self.object.slug})
def get_success_url(self):
return reverse('companies:company_detail', kwargs={'slug': self.object.slug})
class ManufacturerCreateView(LoginRequiredMixin, CreateView):
model = Manufacturer
model: Type[Manufacturer] = Manufacturer
form_class = ManufacturerForm
template_name = 'companies/manufacturer_form.html'
template_name = "companies/manufacturer_form.html"
object: Optional[Manufacturer]
def form_valid(self, form):
cleaned_data = form.cleaned_data.copy()
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Manufacturer),
submission_type='CREATE',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
def form_valid(self, form: ManufacturerForm) -> HttpResponseRedirect:
success_url = reverse(
"companies:manufacturer_detail", kwargs={"slug": form.instance.slug}
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
self.object = form.save()
submission.object_id = self.object.id
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
messages.success(self.request, f'Successfully created {self.object.name}')
return HttpResponseRedirect(self.get_success_url())
messages.success(self.request, 'Your manufacturer submission has been sent for review')
return HttpResponseRedirect(reverse('companies:manufacturer_list'))
return _handle_submission(self.request, form, self.model, success_url)
def get_success_url(self) -> str:
if self.object is None:
return reverse("companies:manufacturer_list")
return reverse(
"companies:manufacturer_detail", kwargs={"slug": self.object.slug}
)
def _handle_update(
request: Any, form: Any, obj: Union[Company, Manufacturer], model: ModelType
) -> HttpResponseRedirect:
"""Helper method to handle update submissions"""
cleaned_data = form.cleaned_data.copy()
submission = EditSubmission.objects.create(
user=request.user,
content_type=ContentType.objects.get_for_model(model),
object_id=obj.pk,
submission_type="EDIT",
changes=cleaned_data,
reason=request.POST.get("reason", ""),
source=request.POST.get("source", ""),
)
# Get user role safely
user_role = getattr(request.user, "role", None)
# If user is moderator or above, auto-approve
if user_role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
obj = form.save()
submission.status = "APPROVED"
submission.handled_by = request.user
submission.save()
messages.success(request, f'Successfully updated {getattr(obj, "name", "")}')
return HttpResponseRedirect(
reverse(
f"companies:{model.__name__.lower()}_detail",
kwargs={"slug": getattr(obj, "slug", "")},
)
)
messages.success(
request, f'Your changes to {getattr(obj, "name", "")} have been sent for review'
)
return HttpResponseRedirect(
reverse(
f"companies:{model.__name__.lower()}_detail",
kwargs={"slug": getattr(obj, "slug", "")},
)
)
def get_success_url(self):
return reverse('companies:manufacturer_detail', kwargs={'slug': self.object.slug})
# Update Views
class CompanyUpdateView(LoginRequiredMixin, UpdateView):
model = Company
model: Type[Company] = Company
form_class = CompanyForm
template_name = 'companies/company_form.html'
template_name = "companies/company_form.html"
object: Optional[Company]
def get_context_data(self, **kwargs):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context['is_edit'] = True
context["is_edit"] = True
return context
def form_valid(self, form):
cleaned_data = form.cleaned_data.copy()
def form_valid(self, form: CompanyForm) -> HttpResponseRedirect:
if self.object is None:
return HttpResponseRedirect(reverse("companies:company_list"))
return _handle_update(self.request, form, self.object, self.model)
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Company),
object_id=self.object.id,
submission_type='EDIT',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
self.object = form.save()
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
messages.success(self.request, f'Successfully updated {self.object.name}')
return HttpResponseRedirect(self.get_success_url())
messages.success(self.request, f'Your changes to {self.object.name} have been sent for review')
return HttpResponseRedirect(reverse('companies:company_detail', kwargs={'slug': self.object.slug}))
def get_success_url(self) -> str:
if self.object is None:
return reverse("companies:company_list")
return reverse("companies:company_detail", kwargs={"slug": self.object.slug})
def get_success_url(self):
return reverse('companies:company_detail', kwargs={'slug': self.object.slug})
class ManufacturerUpdateView(LoginRequiredMixin, UpdateView):
model = Manufacturer
model: Type[Manufacturer] = Manufacturer
form_class = ManufacturerForm
template_name = 'companies/manufacturer_form.html'
template_name = "companies/manufacturer_form.html"
object: Optional[Manufacturer]
def get_context_data(self, **kwargs):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context['is_edit'] = True
context["is_edit"] = True
return context
def form_valid(self, form):
cleaned_data = form.cleaned_data.copy()
def form_valid(self, form: ManufacturerForm) -> HttpResponseRedirect:
if self.object is None:
return HttpResponseRedirect(reverse("companies:manufacturer_list"))
return _handle_update(self.request, form, self.object, self.model)
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Manufacturer),
object_id=self.object.id,
submission_type='EDIT',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
def get_success_url(self) -> str:
if self.object is None:
return reverse("companies:manufacturer_list")
return reverse(
"companies:manufacturer_detail", kwargs={"slug": self.object.slug}
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
self.object = form.save()
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
messages.success(self.request, f'Successfully updated {self.object.name}')
return HttpResponseRedirect(self.get_success_url())
messages.success(self.request, f'Your changes to {self.object.name} have been sent for review')
return HttpResponseRedirect(reverse('companies:manufacturer_detail', kwargs={'slug': self.object.slug}))
def get_success_url(self):
return reverse('companies:manufacturer_detail', kwargs={'slug': self.object.slug})