mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 18:31:09 -05:00
This commit is contained in:
287
parks/views.py
287
parks/views.py
@@ -2,8 +2,11 @@ import requests
|
|||||||
from decimal import Decimal, ROUND_DOWN
|
from decimal import Decimal, ROUND_DOWN
|
||||||
from typing import Any, Optional, cast, Literal
|
from typing import Any, Optional, cast, Literal
|
||||||
|
|
||||||
# Constants for error messages
|
# Constants
|
||||||
|
PARK_DETAIL_URL = "parks:park_detail"
|
||||||
|
PARK_LIST_ITEM_TEMPLATE = "parks/partials/park_list_item.html"
|
||||||
REQUIRED_FIELDS_ERROR = "Please correct the errors below. Required fields are marked with an asterisk (*)."
|
REQUIRED_FIELDS_ERROR = "Please correct the errors below. Required fields are marked with an asterisk (*)."
|
||||||
|
ALLOWED_ROLES = ["MODERATOR", "ADMIN", "SUPERUSER"]
|
||||||
|
|
||||||
from django.views.generic import DetailView, ListView, CreateView, UpdateView
|
from django.views.generic import DetailView, ListView, CreateView, UpdateView
|
||||||
from decimal import InvalidOperation
|
from decimal import InvalidOperation
|
||||||
@@ -284,7 +287,7 @@ def search_parks(request: HttpRequest) -> HttpResponse:
|
|||||||
|
|
||||||
response = render(
|
response = render(
|
||||||
request,
|
request,
|
||||||
"parks/partials/park_list_item.html",
|
PARK_LIST_ITEM_TEMPLATE,
|
||||||
{
|
{
|
||||||
"parks": parks,
|
"parks": parks,
|
||||||
"view_mode": current_view_mode,
|
"view_mode": current_view_mode,
|
||||||
@@ -298,7 +301,7 @@ def search_parks(request: HttpRequest) -> HttpResponse:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
response = render(
|
response = render(
|
||||||
request,
|
request,
|
||||||
"parks/partials/park_list_item.html",
|
PARK_LIST_ITEM_TEMPLATE,
|
||||||
{
|
{
|
||||||
"parks": [],
|
"parks": [],
|
||||||
"error": f"Error performing search: {str(e)}",
|
"error": f"Error performing search: {str(e)}",
|
||||||
@@ -354,7 +357,7 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
if hasattr(self.request.user, "role") and getattr(
|
if hasattr(self.request.user, "role") and getattr(
|
||||||
self.request.user, "role", None
|
self.request.user, "role", None
|
||||||
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
) in ALLOWED_ROLES:
|
||||||
try:
|
try:
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
submission.object_id = self.object.id
|
submission.object_id = self.object.id
|
||||||
@@ -420,7 +423,7 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
|||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
return reverse(PARK_DETAIL_URL, kwargs={"slug": self.object.slug})
|
||||||
|
|
||||||
|
|
||||||
class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
@@ -475,7 +478,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
|
|
||||||
if hasattr(self.request.user, "role") and getattr(
|
if hasattr(self.request.user, "role") and getattr(
|
||||||
self.request.user, "role", None
|
self.request.user, "role", None
|
||||||
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
) in ALLOWED_ROLES:
|
||||||
try:
|
try:
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
submission.status = "APPROVED"
|
submission.status = "APPROVED"
|
||||||
@@ -542,7 +545,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
"You will be notified when they are approved.",
|
"You will be notified when they are approved.",
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
reverse(PARK_DETAIL_URL, kwargs={"slug": self.object.slug})
|
||||||
)
|
)
|
||||||
|
|
||||||
def form_invalid(self, form: ParkForm) -> HttpResponse:
|
def form_invalid(self, form: ParkForm) -> HttpResponse:
|
||||||
@@ -556,7 +559,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
return reverse(PARK_DETAIL_URL, kwargs={"slug": self.object.slug})
|
||||||
|
|
||||||
|
|
||||||
class ParkDetailView(
|
class ParkDetailView(
|
||||||
@@ -611,7 +614,7 @@ class ParkDetailView(
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_redirect_url_pattern(self) -> str:
|
def get_redirect_url_pattern(self) -> str:
|
||||||
return "parks:park_detail"
|
return PARK_DETAIL_URL
|
||||||
|
|
||||||
|
|
||||||
class ParkAreaDetailView(
|
class ParkAreaDetailView(
|
||||||
@@ -643,274 +646,10 @@ class ParkAreaDetailView(
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_redirect_url_pattern(self) -> str:
|
def get_redirect_url_pattern(self) -> str:
|
||||||
return "parks:park_detail"
|
return PARK_DETAIL_URL
|
||||||
|
|
||||||
def get_redirect_url_kwargs(self) -> dict[str, str]:
|
def get_redirect_url_kwargs(self) -> dict[str, str]:
|
||||||
area = cast(ParkArea, self.object)
|
area = cast(ParkArea, self.object)
|
||||||
return {"park_slug": area.park.slug, "area_slug": area.slug}
|
return {"park_slug": area.park.slug, "area_slug": area.slug}
|
||||||
|
|
||||||
def form_valid(self, form: ParkForm) -> HttpResponse:
|
|
||||||
self.normalize_coordinates(form)
|
|
||||||
changes = self.prepare_changes_data(form.cleaned_data)
|
|
||||||
|
|
||||||
submission = EditSubmission.objects.create(
|
|
||||||
user=self.request.user,
|
|
||||||
content_type=ContentType.objects.get_for_model(Park),
|
|
||||||
submission_type="CREATE",
|
|
||||||
changes=changes,
|
|
||||||
reason=self.request.POST.get("reason", ""),
|
|
||||||
source=self.request.POST.get("source", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasattr(self.request.user, "role") and getattr(
|
|
||||||
self.request.user, "role", None
|
|
||||||
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
|
||||||
try:
|
|
||||||
self.object = form.save()
|
|
||||||
submission.object_id = self.object.id
|
|
||||||
submission.status = "APPROVED"
|
|
||||||
submission.handled_by = self.request.user
|
|
||||||
submission.save()
|
|
||||||
|
|
||||||
if form.cleaned_data.get("latitude") and form.cleaned_data.get(
|
|
||||||
"longitude"
|
|
||||||
):
|
|
||||||
Location.objects.create(
|
|
||||||
content_type=ContentType.objects.get_for_model(Park),
|
|
||||||
object_id=self.object.id,
|
|
||||||
name=self.object.name,
|
|
||||||
location_type="park",
|
|
||||||
latitude=form.cleaned_data["latitude"],
|
|
||||||
longitude=form.cleaned_data["longitude"],
|
|
||||||
street_address=form.cleaned_data.get(
|
|
||||||
"street_address", ""),
|
|
||||||
city=form.cleaned_data.get("city", ""),
|
|
||||||
state=form.cleaned_data.get("state", ""),
|
|
||||||
country=form.cleaned_data.get("country", ""),
|
|
||||||
postal_code=form.cleaned_data.get("postal_code", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
photos = self.request.FILES.getlist("photos")
|
|
||||||
uploaded_count = 0
|
|
||||||
for photo_file in photos:
|
|
||||||
try:
|
|
||||||
Photo.objects.create(
|
|
||||||
image=photo_file,
|
|
||||||
uploaded_by=self.request.user,
|
|
||||||
content_type=ContentType.objects.get_for_model(
|
|
||||||
Park),
|
|
||||||
object_id=self.object.id,
|
|
||||||
)
|
|
||||||
uploaded_count += 1
|
|
||||||
except Exception as e:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
f"Error uploading photo {photo_file.name}: {str(e)}",
|
|
||||||
)
|
|
||||||
|
|
||||||
messages.success(
|
|
||||||
self.request,
|
|
||||||
f"Successfully created {self.object.name}. "
|
|
||||||
f"Added {uploaded_count} photo(s).",
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
except Exception as e:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
f"Error creating park: {str(e)}. Please check your input and try again.",
|
|
||||||
)
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
messages.success(
|
|
||||||
self.request,
|
|
||||||
"Your park submission has been sent for review. "
|
|
||||||
"You will be notified when it is approved.",
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(reverse("parks:park_list"))
|
|
||||||
|
|
||||||
def form_invalid(self, form: ParkForm) -> HttpResponse:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
REQUIRED_FIELDS_ERROR,
|
|
||||||
)
|
|
||||||
for field, errors in form.errors.items():
|
|
||||||
for error in errors:
|
|
||||||
messages.error(self.request, f"{field}: {error}")
|
|
||||||
return super().form_invalid(form)
|
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
|
||||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
|
||||||
|
|
||||||
|
|
||||||
class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Park
|
|
||||||
form_class = ParkForm
|
|
||||||
template_name = "parks/park_form.html"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context["is_edit"] = True
|
|
||||||
return context
|
|
||||||
|
|
||||||
def prepare_changes_data(self, cleaned_data: dict[str, Any]) -> dict[str, Any]:
|
|
||||||
data = cleaned_data.copy()
|
|
||||||
if data.get("owner"):
|
|
||||||
data["owner"] = data["owner"].id
|
|
||||||
if data.get("opening_date"):
|
|
||||||
data["opening_date"] = data["opening_date"].isoformat()
|
|
||||||
if data.get("closing_date"):
|
|
||||||
data["closing_date"] = data["closing_date"].isoformat()
|
|
||||||
decimal_fields = ["latitude", "longitude",
|
|
||||||
"size_acres", "average_rating"]
|
|
||||||
for field in decimal_fields:
|
|
||||||
if data.get(field):
|
|
||||||
data[field] = str(data[field])
|
|
||||||
return data
|
|
||||||
|
|
||||||
def normalize_coordinates(self, form: ParkForm) -> None:
|
|
||||||
if form.cleaned_data.get("latitude"):
|
|
||||||
lat = Decimal(str(form.cleaned_data["latitude"]))
|
|
||||||
form.cleaned_data["latitude"] = lat.quantize(
|
|
||||||
Decimal("0.000001"), rounding=ROUND_DOWN
|
|
||||||
)
|
|
||||||
if form.cleaned_data.get("longitude"):
|
|
||||||
lon = Decimal(str(form.cleaned_data["longitude"]))
|
|
||||||
form.cleaned_data["longitude"] = lon.quantize(
|
|
||||||
Decimal("0.000001"), rounding=ROUND_DOWN
|
|
||||||
)
|
|
||||||
|
|
||||||
def form_valid(self, form: ParkForm) -> HttpResponse:
|
|
||||||
self.normalize_coordinates(form)
|
|
||||||
changes = self.prepare_changes_data(form.cleaned_data)
|
|
||||||
|
|
||||||
submission = EditSubmission.objects.create(
|
|
||||||
user=self.request.user,
|
|
||||||
content_type=ContentType.objects.get_for_model(Park),
|
|
||||||
object_id=self.object.id,
|
|
||||||
submission_type="EDIT",
|
|
||||||
changes=changes,
|
|
||||||
reason=self.request.POST.get("reason", ""),
|
|
||||||
source=self.request.POST.get("source", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasattr(self.request.user, "role") and getattr(
|
|
||||||
self.request.user, "role", None
|
|
||||||
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
|
||||||
try:
|
|
||||||
self.object = form.save()
|
|
||||||
submission.status = "APPROVED"
|
|
||||||
submission.handled_by = self.request.user
|
|
||||||
submission.save()
|
|
||||||
|
|
||||||
location_data = {
|
|
||||||
"name": self.object.name,
|
|
||||||
"location_type": "park",
|
|
||||||
"latitude": form.cleaned_data.get("latitude"),
|
|
||||||
"longitude": form.cleaned_data.get("longitude"),
|
|
||||||
"street_address": form.cleaned_data.get("street_address", ""),
|
|
||||||
"city": form.cleaned_data.get("city", ""),
|
|
||||||
"state": form.cleaned_data.get("state", ""),
|
|
||||||
"country": form.cleaned_data.get("country", ""),
|
|
||||||
"postal_code": form.cleaned_data.get("postal_code", ""),
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.object.location.exists():
|
|
||||||
location = self.object.location.first()
|
|
||||||
for key, value in location_data.items():
|
|
||||||
setattr(location, key, value)
|
|
||||||
location.save()
|
|
||||||
else:
|
|
||||||
Location.objects.create(
|
|
||||||
content_type=ContentType.objects.get_for_model(Park),
|
|
||||||
object_id=self.object.id,
|
|
||||||
**location_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
photos = self.request.FILES.getlist("photos")
|
|
||||||
uploaded_count = 0
|
|
||||||
for photo_file in photos:
|
|
||||||
try:
|
|
||||||
Photo.objects.create(
|
|
||||||
image=photo_file,
|
|
||||||
uploaded_by=self.request.user,
|
|
||||||
content_type=ContentType.objects.get_for_model(
|
|
||||||
Park),
|
|
||||||
object_id=self.object.id,
|
|
||||||
)
|
|
||||||
uploaded_count += 1
|
|
||||||
except Exception as e:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
f"Error uploading photo {photo_file.name}: {str(e)}",
|
|
||||||
)
|
|
||||||
|
|
||||||
messages.success(
|
|
||||||
self.request,
|
|
||||||
f"Successfully updated {self.object.name}. "
|
|
||||||
f"Added {uploaded_count} new photo(s).",
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
except Exception as e:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
f"Error updating park: {str(e)}. Please check your input and try again.",
|
|
||||||
)
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
messages.success(
|
|
||||||
self.request,
|
|
||||||
f"Your changes to {self.object.name} have been sent for review. "
|
|
||||||
"You will be notified when they are approved.",
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(
|
|
||||||
reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
|
||||||
)
|
|
||||||
|
|
||||||
def form_invalid(self, form: ParkForm) -> HttpResponse:
|
|
||||||
messages.error(
|
|
||||||
self.request,
|
|
||||||
REQUIRED_FIELDS_ERROR,
|
|
||||||
)
|
|
||||||
for field, errors in form.errors.items():
|
|
||||||
for error in errors:
|
|
||||||
messages.error(self.request, f"{field}: {error}")
|
|
||||||
return super().form_invalid(form)
|
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
|
||||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
|
||||||
|
|
||||||
|
|
||||||
class ParkAreaDetailView(
|
|
||||||
SlugRedirectMixin,
|
|
||||||
EditSubmissionMixin,
|
|
||||||
PhotoSubmissionMixin,
|
|
||||||
HistoryMixin,
|
|
||||||
DetailView,
|
|
||||||
):
|
|
||||||
model = ParkArea
|
|
||||||
template_name = "parks/area_detail.html"
|
|
||||||
context_object_name = "area"
|
|
||||||
slug_url_kwarg = "area_slug"
|
|
||||||
|
|
||||||
def get_object(self, queryset: Optional[QuerySet[ParkArea]] = None) -> ParkArea:
|
|
||||||
if queryset is None:
|
|
||||||
queryset = self.get_queryset()
|
|
||||||
park_slug = self.kwargs.get("park_slug")
|
|
||||||
area_slug = self.kwargs.get("area_slug")
|
|
||||||
if park_slug is None or area_slug is None:
|
|
||||||
raise ObjectDoesNotExist("Missing slug")
|
|
||||||
area, _ = ParkArea.get_by_slug(area_slug)
|
|
||||||
if area.park.slug != park_slug:
|
|
||||||
raise ObjectDoesNotExist("Park slug doesn't match")
|
|
||||||
return area
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_redirect_url_pattern(self) -> str:
|
|
||||||
return "parks:park_detail"
|
|
||||||
|
|
||||||
def get_redirect_url_kwargs(self) -> dict[str, str]:
|
|
||||||
area = cast(ParkArea, self.object)
|
|
||||||
return {"park_slug": area.park.slug, "area_slug": area.slug}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user