Add operators and property owners functionality

- Implemented OperatorListView and OperatorDetailView for managing operators.
- Created corresponding templates for operator listing and detail views.
- Added PropertyOwnerListView and PropertyOwnerDetailView for managing property owners.
- Developed templates for property owner listing and detail views.
- Established relationships between parks and operators, and parks and property owners in the models.
- Created migrations to reflect the new relationships and fields in the database.
- Added admin interfaces for PropertyOwner management.
- Implemented tests for operators and property owners.
This commit is contained in:
pacnpal
2025-07-04 14:49:36 -04:00
parent 8360f3fd43
commit 751cd86a31
80 changed files with 2943 additions and 2358 deletions

View File

@@ -10,7 +10,7 @@ from django.utils.datastructures import MultiValueDict
from django.http import QueryDict
from .models import EditSubmission, PhotoSubmission
from .mixins import EditSubmissionMixin, PhotoSubmissionMixin, ModeratorRequiredMixin, AdminRequiredMixin, InlineEditMixin, HistoryMixin
from companies.models import Company
from operators.models import Operator
from django.views.generic import DetailView
from django.test import RequestFactory
import json
@@ -19,7 +19,7 @@ from typing import Optional
User = get_user_model()
class TestView(EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
model = Company
model = Operator
template_name = 'test.html'
pk_url_kwarg = 'pk'
slug_url_kwarg = 'slug'
@@ -58,8 +58,8 @@ class ModerationMixinsTests(TestCase):
)
# Create test company
self.company = Company.objects.create(
name='Test Company',
self.operator = Operator.objects.create(
name='Test Operator',
website='http://example.com',
headquarters='Test HQ',
description='Test Description'
@@ -68,10 +68,10 @@ class ModerationMixinsTests(TestCase):
def test_edit_submission_mixin_unauthenticated(self):
"""Test edit submission when not logged in"""
view = TestView()
request = self.factory.post(f'/test/{self.company.pk}/')
request = self.factory.post(f'/test/{self.operator.pk}/')
request.user = AnonymousUser()
view.setup(request, pk=self.company.pk)
view.kwargs = {'pk': self.company.pk}
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
response = view.handle_edit_submission(request, {})
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 403)
@@ -80,13 +80,13 @@ class ModerationMixinsTests(TestCase):
"""Test edit submission with no changes"""
view = TestView()
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data=json.dumps({}),
content_type='application/json'
)
request.user = self.user
view.setup(request, pk=self.company.pk)
view.kwargs = {'pk': self.company.pk}
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
response = view.post(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 400)
@@ -95,13 +95,13 @@ class ModerationMixinsTests(TestCase):
"""Test edit submission with invalid JSON"""
view = TestView()
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data='invalid json',
content_type='application/json'
)
request.user = self.user
view.setup(request, pk=self.company.pk)
view.kwargs = {'pk': self.company.pk}
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
response = view.post(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 400)
@@ -109,10 +109,10 @@ class ModerationMixinsTests(TestCase):
def test_edit_submission_mixin_regular_user(self):
"""Test edit submission as regular user"""
view = TestView()
request = self.factory.post(f'/test/{self.company.pk}/')
request = self.factory.post(f'/test/{self.operator.pk}/')
request.user = self.user
view.setup(request, pk=self.company.pk)
view.kwargs = {'pk': self.company.pk}
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
changes = {'name': 'New Name'}
response = view.handle_edit_submission(request, changes, 'Test reason', 'Test source')
self.assertIsInstance(response, JsonResponse)
@@ -123,10 +123,10 @@ class ModerationMixinsTests(TestCase):
def test_edit_submission_mixin_moderator(self):
"""Test edit submission as moderator"""
view = TestView()
request = self.factory.post(f'/test/{self.company.pk}/')
request = self.factory.post(f'/test/{self.operator.pk}/')
request.user = self.moderator
view.setup(request, pk=self.company.pk)
view.kwargs = {'pk': self.company.pk}
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
changes = {'name': 'New Name'}
response = view.handle_edit_submission(request, changes, 'Test reason', 'Test source')
self.assertIsInstance(response, JsonResponse)
@@ -137,16 +137,16 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_unauthenticated(self):
"""Test photo submission when not logged in"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data={},
format='multipart'
)
request.user = AnonymousUser()
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 403)
@@ -154,16 +154,16 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_no_photo(self):
"""Test photo submission with no photo"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data={},
format='multipart'
)
request.user = self.user
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 400)
@@ -171,8 +171,8 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_regular_user(self):
"""Test photo submission as regular user"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
# Create a test photo file
photo = SimpleUploadedFile(
@@ -182,12 +182,12 @@ class ModerationMixinsTests(TestCase):
)
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'},
format='multipart'
)
request.user = self.user
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
@@ -198,8 +198,8 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_moderator(self):
"""Test photo submission as moderator"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
# Create a test photo file
photo = SimpleUploadedFile(
@@ -209,12 +209,12 @@ class ModerationMixinsTests(TestCase):
)
request = self.factory.post(
f'/test/{self.company.pk}/',
f'/test/{self.operator.pk}/',
data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'},
format='multipart'
)
request.user = self.moderator
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
@@ -281,26 +281,26 @@ class ModerationMixinsTests(TestCase):
def test_inline_edit_mixin(self):
"""Test inline edit mixin"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
# Test unauthenticated user
request = self.factory.get(f'/test/{self.company.pk}/')
request = self.factory.get(f'/test/{self.operator.pk}/')
request.user = AnonymousUser()
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertNotIn('can_edit', context)
# Test regular user
request.user = self.user
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertTrue(context['can_edit'])
self.assertFalse(context['can_auto_approve'])
# Test moderator
request.user = self.moderator
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertTrue(context['can_edit'])
self.assertTrue(context['can_auto_approve'])
@@ -308,17 +308,17 @@ class ModerationMixinsTests(TestCase):
def test_history_mixin(self):
"""Test history mixin"""
view = TestView()
view.kwargs = {'pk': self.company.pk}
view.object = self.company
request = self.factory.get(f'/test/{self.company.pk}/')
view.kwargs = {'pk': self.operator.pk}
view.object = self.operator
request = self.factory.get(f'/test/{self.operator.pk}/')
request.user = self.user
view.setup(request, pk=self.company.pk)
view.setup(request, pk=self.operator.pk)
# Create some edit submissions
EditSubmission.objects.create(
user=self.user,
content_type=ContentType.objects.get_for_model(Company),
object_id=getattr(self.company, 'id', None),
content_type=ContentType.objects.get_for_model(Operator),
object_id=getattr(self.operator, 'id', None),
submission_type='EDIT',
changes={'name': 'New Name'},
status='APPROVED'