Files
thrillwiki_django_no_react/moderation/views.py
2024-11-13 21:59:49 +00:00

404 lines
16 KiB
Python

from django.views.generic import ListView, TemplateView
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, JsonResponse, HttpRequest
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
from django.db.models import Q
from django.core.exceptions import PermissionDenied
from typing import Optional, Any, cast
from accounts.models import User
from .models import EditSubmission, PhotoSubmission
from parks.models import Park, ParkArea
from designers.models import Designer
from companies.models import Manufacturer
from rides.models import RideModel
MODERATOR_ROLES = ['MODERATOR', 'ADMIN', 'SUPERUSER']
class ModeratorRequiredMixin(UserPassesTestMixin):
request: HttpRequest
def test_func(self) -> bool:
"""Check if user has moderator permissions."""
user = cast(User, self.request.user)
return (
user.is_authenticated and
(getattr(user, 'role', None) in MODERATOR_ROLES or user.is_superuser)
)
def handle_no_permission(self) -> HttpResponse:
if not self.request.user.is_authenticated:
return super().handle_no_permission()
raise PermissionDenied("You do not have moderator permissions.")
@login_required
def search_parks(request: HttpRequest) -> HttpResponse:
"""HTMX endpoint for searching parks in moderation dashboard"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
query = request.GET.get('q', '').strip()
submission_id = request.GET.get('submission_id')
# If no query, show first 10 parks
if not query:
parks = Park.objects.all().order_by('name')[:10]
else:
parks = Park.objects.filter(name__icontains=query).order_by('name')[:10]
context = {
'parks': parks,
'search_term': query,
'submission_id': submission_id
}
return render(request, 'moderation/partials/park_search_results.html', context)
@login_required
def search_manufacturers(request: HttpRequest) -> HttpResponse:
"""HTMX endpoint for searching manufacturers in moderation dashboard"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
query = request.GET.get('q', '').strip()
submission_id = request.GET.get('submission_id')
# If no query, show first 10 manufacturers
if not query:
manufacturers = Manufacturer.objects.all().order_by('name')[:10]
else:
manufacturers = Manufacturer.objects.filter(name__icontains=query).order_by('name')[:10]
context = {
'manufacturers': manufacturers,
'search_term': query,
'submission_id': submission_id
}
return render(request, 'moderation/partials/manufacturer_search_results.html', context)
@login_required
def search_designers(request: HttpRequest) -> HttpResponse:
"""HTMX endpoint for searching designers in moderation dashboard"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
query = request.GET.get('q', '').strip()
submission_id = request.GET.get('submission_id')
# If no query, show first 10 designers
if not query:
designers = Designer.objects.all().order_by('name')[:10]
else:
designers = Designer.objects.filter(name__icontains=query).order_by('name')[:10]
context = {
'designers': designers,
'search_term': query,
'submission_id': submission_id
}
return render(request, 'moderation/partials/designer_search_results.html', context)
@login_required
def search_ride_models(request: HttpRequest) -> HttpResponse:
"""HTMX endpoint for searching ride models in moderation dashboard"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
query = request.GET.get('q', '').strip()
submission_id = request.GET.get('submission_id')
manufacturer_id = request.GET.get('manufacturer')
queryset = RideModel.objects.all()
if manufacturer_id:
queryset = queryset.filter(manufacturer_id=manufacturer_id)
# If no query, show first 10 models
if not query:
ride_models = queryset.order_by('name')[:10]
else:
ride_models = queryset.filter(name__icontains=query).order_by('name')[:10]
context = {
'ride_models': ride_models,
'search_term': query,
'submission_id': submission_id
}
return render(request, 'moderation/partials/ride_model_search_results.html', context)
class DashboardView(LoginRequiredMixin, ModeratorRequiredMixin, ListView):
template_name = 'moderation/dashboard.html'
context_object_name = 'submissions'
paginate_by = 10
def get_template_names(self):
if self.request.headers.get('HX-Request'):
return ['moderation/partials/dashboard_content.html']
return [self.template_name]
def get_queryset(self):
status = self.request.GET.get('status', 'PENDING')
submission_type = self.request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := self.request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := self.request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
return queryset
@login_required
def submission_list(request: HttpRequest) -> HttpResponse:
"""View for submission list with filters"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
status = request.GET.get('status', 'PENDING')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
'parks': [(p.id, str(p)) for p in Park.objects.all()],
'designers': [(d.id, str(d)) for d in Designer.objects.all()],
'manufacturers': [(m.id, str(m)) for m in Manufacturer.objects.all()],
'ride_models': [(m.id, str(m)) for m in RideModel.objects.all()],
'owners': [(u.id, str(u)) for u in User.objects.filter(role__in=['OWNER', 'ADMIN', 'SUPERUSER'])]
}
# If it's an HTMX request, return just the content
if request.headers.get('HX-Request'):
return render(request, 'moderation/partials/dashboard_content.html', context)
# For direct URL access, return the full dashboard template
return render(request, 'moderation/dashboard.html', context)
@login_required
def edit_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for editing a submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(EditSubmission, id=submission_id)
if request.method == 'POST':
# Handle the edit submission
notes = request.POST.get('notes')
if not notes:
return HttpResponse("Notes are required when editing a submission", status=400)
try:
# Update the moderator_changes with the edited values
edited_changes = submission.changes.copy()
for field in submission.changes.keys():
if field == 'stats':
edited_stats = {}
for key in submission.changes['stats'].keys():
if new_value := request.POST.get(f'stats.{key}'):
edited_stats[key] = new_value
edited_changes['stats'] = edited_stats
else:
if new_value := request.POST.get(field):
# Handle special field types
if field in ['latitude', 'longitude', 'size_acres']:
try:
edited_changes[field] = float(new_value)
except ValueError:
return HttpResponse(f"Invalid value for {field}", status=400)
else:
edited_changes[field] = new_value
submission.moderator_changes = edited_changes
submission.notes = notes
submission.save()
# Return the updated submission
context = {
'submission': submission,
'user': request.user,
'parks': [(p.id, str(p)) for p in Park.objects.all()],
'designers': [(d.id, str(d)) for d in Designer.objects.all()],
'manufacturers': [(m.id, str(m)) for m in Manufacturer.objects.all()],
'ride_models': [(m.id, str(m)) for m in RideModel.objects.all()],
'owners': [(u.id, str(u)) for u in User.objects.filter(role__in=['OWNER', 'ADMIN', 'SUPERUSER'])],
'park_areas': [(a.id, str(a)) for a in ParkArea.objects.filter(park_id=edited_changes.get('park'))] if edited_changes.get('park') else []
}
return render(request, 'moderation/partials/submission_list.html', context)
except Exception as e:
return HttpResponse(str(e), status=400)
return HttpResponse("Invalid request method", status=405)
@login_required
def approve_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for approving a submission"""
user = cast(User, request.user)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED' and not (user.role in ['ADMIN', 'SUPERUSER'] or user.is_superuser):
return HttpResponse("Only admins can handle escalated submissions", status=403)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
try:
submission.approve(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'PENDING')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
except ValueError as e:
return HttpResponse(str(e), status=400)
@login_required
def reject_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for rejecting a submission"""
user = cast(User, request.user)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED' and not (user.role in ['ADMIN', 'SUPERUSER'] or user.is_superuser):
return HttpResponse("Only admins can handle escalated submissions", status=403)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission.reject(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'PENDING')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
@login_required
def escalate_submission(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for escalating a submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(EditSubmission, id=submission_id)
if submission.status == 'ESCALATED':
return HttpResponse("Submission is already escalated", status=400)
submission.escalate(user)
_update_submission_notes(submission, request.POST.get('notes'))
# Get updated queryset with filters
status = request.GET.get('status', 'PENDING')
submission_type = request.GET.get('submission_type', '')
if submission_type == 'photo':
queryset = PhotoSubmission.objects.filter(status=status).order_by('-created_at')
else:
queryset = EditSubmission.objects.filter(status=status).order_by('-created_at')
if type_filter := request.GET.get('type'):
queryset = queryset.filter(submission_type=type_filter)
if content_type := request.GET.get('content_type'):
queryset = queryset.filter(content_type__model=content_type)
context = {
'submissions': queryset,
'user': request.user,
}
return render(request, 'moderation/partials/dashboard_content.html', context)
@login_required
def approve_photo(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for approving a photo submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(PhotoSubmission, id=submission_id)
try:
submission.approve(user, request.POST.get('notes', ''))
return render(request, 'moderation/partials/photo_submission.html', {'submission': submission})
except Exception as e:
return HttpResponse(str(e), status=400)
@login_required
def reject_photo(request: HttpRequest, submission_id: int) -> HttpResponse:
"""HTMX endpoint for rejecting a photo submission"""
user = cast(User, request.user)
if not (user.role in MODERATOR_ROLES or user.is_superuser):
return HttpResponse(status=403)
submission = get_object_or_404(PhotoSubmission, id=submission_id)
submission.reject(user, request.POST.get('notes', ''))
return render(request, 'moderation/partials/photo_submission.html', {'submission': submission})
def _update_submission_notes(submission: EditSubmission, notes: Optional[str]) -> None:
"""Update submission notes if provided."""
if notes:
submission.notes = notes
submission.save()