This commit is contained in:
pacnpal
2025-02-21 19:14:26 -05:00
parent 3523274cbd
commit ef2437b7f4

View File

@@ -2,8 +2,11 @@ import requests
from decimal import Decimal, ROUND_DOWN
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 (*)."
ALLOWED_ROLES = ["MODERATOR", "ADMIN", "SUPERUSER"]
from django.views.generic import DetailView, ListView, CreateView, UpdateView
from decimal import InvalidOperation
@@ -284,7 +287,7 @@ def search_parks(request: HttpRequest) -> HttpResponse:
response = render(
request,
"parks/partials/park_list_item.html",
PARK_LIST_ITEM_TEMPLATE,
{
"parks": parks,
"view_mode": current_view_mode,
@@ -298,7 +301,7 @@ def search_parks(request: HttpRequest) -> HttpResponse:
except Exception as e:
response = render(
request,
"parks/partials/park_list_item.html",
PARK_LIST_ITEM_TEMPLATE,
{
"parks": [],
"error": f"Error performing search: {str(e)}",
@@ -354,7 +357,7 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
if hasattr(self.request.user, "role") and getattr(
self.request.user, "role", None
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
) in ALLOWED_ROLES:
try:
self.object = form.save()
submission.object_id = self.object.id
@@ -420,7 +423,7 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
return super().form_invalid(form)
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):
@@ -475,7 +478,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
if hasattr(self.request.user, "role") and getattr(
self.request.user, "role", None
) in ["MODERATOR", "ADMIN", "SUPERUSER"]:
) in ALLOWED_ROLES:
try:
self.object = form.save()
submission.status = "APPROVED"
@@ -542,7 +545,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
"You will be notified when they are approved.",
)
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:
@@ -556,7 +559,7 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
return super().form_invalid(form)
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(
@@ -611,7 +614,7 @@ class ParkDetailView(
return context
def get_redirect_url_pattern(self) -> str:
return "parks:park_detail"
return PARK_DETAIL_URL
class ParkAreaDetailView(
@@ -643,274 +646,10 @@ class ParkAreaDetailView(
return context
def get_redirect_url_pattern(self) -> str:
return "parks:park_detail"
return PARK_DETAIL_URL
def get_redirect_url_kwargs(self) -> dict[str, str]:
area = cast(ParkArea, self.object)
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}