From 897d8c6ea966f1143288befb582d470bc30a085a Mon Sep 17 00:00:00 2001 From: SebF Date: Fri, 4 Apr 2025 10:17:59 +0200 Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20des=20vues=20places?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/urls.py | 106 +++++----- src/agenda_culturel/views/__init__.py | 1 + src/agenda_culturel/views/oldviews.py | 237 +-------------------- src/agenda_culturel/views/places_views.py | 243 ++++++++++++++++++++++ 4 files changed, 299 insertions(+), 288 deletions(-) create mode 100644 src/agenda_culturel/views/places_views.py diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index 1c236ce..8513e5c 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -46,6 +46,18 @@ from .views import ( rename_tag, delete_tag, TagCreateView, + # Places + PlaceCreateView, + PlaceDeleteView, + PlaceDetailView, + PlaceDetailViewPast, + PlaceFromEventCreateView, + PlaceListAdminView, + PlaceListView, + PlaceUpdateView, + UnknownPlaceAddView, + UnknownPlacesListView, + fix_unknown_places, # TODO pas encore triƩ home, week_view, @@ -56,16 +68,7 @@ from .views import ( recent, administration, activite, - PlaceDeleteView, - PlaceDetailView, - PlaceDetailViewPast, - PlaceUpdateView, - PlaceListView, - PlaceListAdminView, - UnknownPlaceAddView, - UnknownPlacesListView, fix_duplicate, - fix_unknown_places, clear_cache, export_event_ical, MessageDeleteView, @@ -93,8 +96,6 @@ from .views import ( event_search_full, recurrent_imports, delete_cm_spam, - PlaceCreateView, - PlaceFromEventCreateView, update_from_source, change_status_event, EventDeleteView, @@ -229,6 +230,48 @@ urlpatterns = [ OrganisationUpdateView.as_view(), name="edit_organisation", ), + # Places + path("places/add", PlaceCreateView.as_view(), name="add_place"), + path("place//delete", PlaceDeleteView.as_view(), name="delete_place"), + path("place/", PlaceDetailView.as_view(), name="view_place"), + path( + "place/-", + PlaceDetailView.as_view(), + name="view_place_fullname", + ), + path( + "place//past", + PlaceDetailViewPast.as_view(), + name="view_place_past", + ), + path( + "place/-/past", + PlaceDetailViewPast.as_view(), + name="view_place_past_fullname", + ), + path( + "places/add/", + PlaceFromEventCreateView.as_view(), + name="add_place_from_event", + ), + path("places/list", PlaceListAdminView.as_view(), name="view_places_admin"), + path("places/", PlaceListView.as_view(), name="view_places"), + path("place//edit", PlaceUpdateView.as_view(), name="edit_place"), + path( + "event//addplace", + UnknownPlaceAddView.as_view(), + name="add_place_to_event", + ), + path( + "events/unknown-places", + UnknownPlacesListView.as_view(), + name="view_unknown_places", + ), + path( + "events/unknown-places/fix", + fix_unknown_places, + name="fix_unknown_places", + ), # TODO pas encore triƩ path("", home, name="home"), path("cat:/", home, name="home_category"), @@ -432,48 +475,7 @@ urlpatterns = [ export_ical, name="export_ical_organisation", ), - path( - "place//past", - PlaceDetailViewPast.as_view(), - name="view_place_past", - ), - path("place/", PlaceDetailView.as_view(), name="view_place"), path("place//ical", export_ical, name="export_ical_place"), - path( - "place/-/past", - PlaceDetailViewPast.as_view(), - name="view_place_past_fullname", - ), - path( - "place/-", - PlaceDetailView.as_view(), - name="view_place_fullname", - ), - path("place//edit", PlaceUpdateView.as_view(), name="edit_place"), - path("place//delete", PlaceDeleteView.as_view(), name="delete_place"), - path("places/", PlaceListView.as_view(), name="view_places"), - path("places/list", PlaceListAdminView.as_view(), name="view_places_admin"), - path("places/add", PlaceCreateView.as_view(), name="add_place"), - path( - "places/add/", - PlaceFromEventCreateView.as_view(), - name="add_place_from_event", - ), - path( - "events/unknown-places", - UnknownPlacesListView.as_view(), - name="view_unknown_places", - ), - path( - "events/unknown-places/fix", - fix_unknown_places, - name="fix_unknown_places", - ), - path( - "event//addplace", - UnknownPlaceAddView.as_view(), - name="add_place_to_event", - ), path( "event/////ical", export_event_ical, diff --git a/src/agenda_culturel/views/__init__.py b/src/agenda_culturel/views/__init__.py index b82016c..7b37988 100644 --- a/src/agenda_culturel/views/__init__.py +++ b/src/agenda_culturel/views/__init__.py @@ -3,4 +3,5 @@ from .errors import * from .general_pages_views import * from .moderation_views import * from .organisations_views import * +from .places_views import * from .tag_views import * diff --git a/src/agenda_culturel/views/oldviews.py b/src/agenda_culturel/views/oldviews.py index 6c9fb6e..d1af9d6 100644 --- a/src/agenda_culturel/views/oldviews.py +++ b/src/agenda_culturel/views/oldviews.py @@ -32,7 +32,7 @@ from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.timezone import datetime from django.utils.translation import gettext_lazy as _ -from django.views.generic import DetailView, ListView +from django.views.generic import DetailView from django.views.generic.edit import ( CreateView, DeleteView, @@ -66,14 +66,12 @@ from ..forms import ( BatchImportationForm, CategorisationForm, CategorisationRuleImportForm, - EventAddPlaceForm, EventForm, EventFormWithContact, FixDuplicates, MergeDuplicates, MessageEventForm, MessageForm, - PlaceForm, RecurrentImportForm, SelectEventInList, SimpleContactForm, @@ -96,7 +94,6 @@ from ..models import ( remove_accents, UserProfile, ) -from ..utils import PlaceGuesser from .utils import get_event_qs logger = logging.getLogger(__name__) @@ -2138,238 +2135,6 @@ def apply_categorisation_rules(request): return HttpResponseRedirect(reverse_lazy("categorisation_rules")) -######################### -## Places -######################### - - -class PlaceListView(ListView): - model = Place - ordering = ["name__unaccent"] - - -class PlaceListAdminView(PermissionRequiredMixin, ListView): - model = Place - paginate_by = 10 - permission_required = "agenda_culturel.add_place" - ordering = ["name__unaccent"] - template_name = "agenda_culturel/place_list_admin.html" - - -class PlaceDetailView(ListView): - model = Place - template_name = "agenda_culturel/place_detail.html" - paginate_by = 10 - - def get_queryset(self): - self.place = get_object_or_404(Place, pk=self.kwargs["pk"]) - return ( - get_event_qs(self.request) - .filter(exact_location=self.place) - .filter( - Q(other_versions__isnull=True) - | Q(other_versions__representative=F("pk")) - | Q(other_versions__representative__isnull=True) - ) - .filter(start_day__gte=datetime.now()) - .order_by("start_day") - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["object"] = self.place - return context - - -class PlaceDetailViewPast(PlaceDetailView): - def get_queryset(self): - self.place = get_object_or_404(Place, pk=self.kwargs["pk"]) - self.past = True - return ( - get_event_qs(self.request) - .filter(exact_location=self.place) - .filter( - Q(other_versions__isnull=True) - | Q(other_versions__representative=F("pk")) - | Q(other_versions__representative__isnull=True) - ) - .filter(start_day__lte=datetime.now()) - .order_by("-start_day") - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["past"] = self.past - return context - - -class UpdatePlaces: - def form_valid(self, form): - result = super().form_valid(form) - p = form.instance - - if not hasattr(self, "nb_applied"): - self.nb_applied = 0 - - # if required, find all matching events - if form.apply(): - self.nb_applied += p.associate_matching_events() - - if self.nb_applied > 1: - messages.success( - self.request, - _("{} events have been updated.").format(self.nb_applied), - ) - elif self.nb_applied == 1: - messages.success(self.request, _("1 event has been updated.")) - else: - messages.info(self.request, _("No events have been modified.")) - return result - - -class PlaceUpdateView( - UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, UpdateView -): - model = Place - permission_required = "agenda_culturel.change_place" - success_message = _("The place has been successfully updated.") - form_class = PlaceForm - - -class PlaceCreateView( - UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, CreateView -): - model = Place - permission_required = "agenda_culturel.add_place" - success_message = _("The place has been successfully created.") - form_class = PlaceForm - - -class PlaceDeleteView(PermissionRequiredMixin, DeleteView): - model = Place - permission_required = "agenda_culturel.delete_place" - success_url = reverse_lazy("view_places_admin") - - -class UnknownPlacesListView(PermissionRequiredMixin, ListView): - model = Event - permission_required = "agenda_culturel.add_place" - paginate_by = 10 - ordering = ["-pk"] - template_name = "agenda_culturel/place_unknown_list.html" - queryset = Event.get_qs_events_with_unkwnon_place() - - -def fix_unknown_places(request): - # get all places - places = Place.objects.all() - # get all events without exact location - u_events = Event.get_qs_events_with_unkwnon_place() - - to_be_updated = [] - # try to find matches - for ue in u_events: - for p in places: - if p.match(ue): - ue.exact_location = p - to_be_updated.append(ue) - continue - # update events with a location - Event.objects.bulk_update(to_be_updated, fields=["exact_location"]) - - # create a success message - nb = len(to_be_updated) - if nb > 1: - messages.success(request, _("{} events have been updated.").format(nb)) - elif nb == 1: - messages.success(request, _("1 event has been updated.")) - else: - messages.info(request, _("No events have been modified.")) - - # come back to the list of places - return HttpResponseRedirect(reverse_lazy("view_unknown_places")) - - -class UnknownPlaceAddView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView): - model = Event - permission_required = ( - "agenda_culturel.change_place", - "agenda_culturel.change_event", - ) - form_class = EventAddPlaceForm - template_name = "agenda_culturel/place_unknown_form.html" - - def form_valid(self, form): - self.modified_event = form.cleaned_data.get("place") - self.add_alias = form.cleaned_data.get("add_alias") - result = super().form_valid(form) - - if form.cleaned_data.get("place"): - messages.success( - self.request, - _("The selected place has been assigned to the event."), - ) - if form.cleaned_data.get("add_alias"): - messages.success( - self.request, - _("A new alias has been added to the selected place."), - ) - - nb_applied = form.cleaned_data.get("place").associate_matching_events() - - if nb_applied > 1: - messages.success( - self.request, - _("{} events have been updated.").format(nb_applied), - ) - elif nb_applied == 1: - messages.success(self.request, _("1 event has been updated.")) - else: - messages.info(self.request, _("No events have been modified.")) - - return result - - def get_success_url(self): - if self.modified_event: - return reverse_lazy("view_unknown_places") - else: - param = "?add=1" if self.add_alias else "" - return reverse_lazy("add_place_from_event", args=[self.object.pk]) + param - - -class PlaceFromEventCreateView(PlaceCreateView): - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["event"] = self.event - return context - - def get_initial(self, *args, **kwargs): - initial = super().get_initial(**kwargs) - self.event = get_object_or_404(Event, pk=self.kwargs["pk"]) - if self.event.location and "add" in self.request.GET: - initial["aliases"] = [self.event.location] - guesser = PlaceGuesser() - name, address, postcode, city = guesser.guess_address_elements( - self.event.location - ) - initial["name"] = name - initial["address"] = address - initial["postcode"] = postcode - initial["city"] = city - initial["location"] = "" - - return initial - - def form_valid(self, form): - result = super().form_valid(form) - self.event.exact_location = form.instance - self.event.save(update_fields=["exact_location"]) - return result - - def get_success_url(self): - return self.event.get_absolute_url() - - def statistics(request, pk=None): if pk is not None: rimport = RecurrentImport.objects.filter(pk=pk) diff --git a/src/agenda_culturel/views/places_views.py b/src/agenda_culturel/views/places_views.py new file mode 100644 index 0000000..d838fd4 --- /dev/null +++ b/src/agenda_culturel/views/places_views.py @@ -0,0 +1,243 @@ +from datetime import datetime + +from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin +from django.db.models import F, Q +from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ +from django.views.generic import ListView, UpdateView, CreateView, DeleteView + +from .utils import get_event_qs +from ..forms import PlaceForm, EventAddPlaceForm +from ..models import Place, Event +from ..utils import PlaceGuesser + + +class PlaceListView(ListView): + model = Place + ordering = ["name__unaccent"] + + +class PlaceListAdminView(PermissionRequiredMixin, ListView): + model = Place + paginate_by = 10 + permission_required = "agenda_culturel.add_place" + ordering = ["name__unaccent"] + template_name = "agenda_culturel/place_list_admin.html" + + +class PlaceDetailView(ListView): + model = Place + template_name = "agenda_culturel/place_detail.html" + paginate_by = 10 + + def get_queryset(self): + self.place = get_object_or_404(Place, pk=self.kwargs["pk"]) + return ( + get_event_qs(self.request) + .filter(exact_location=self.place) + .filter( + Q(other_versions__isnull=True) + | Q(other_versions__representative=F("pk")) + | Q(other_versions__representative__isnull=True) + ) + .filter(start_day__gte=datetime.now()) + .order_by("start_day") + ) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["object"] = self.place + return context + + +class PlaceDetailViewPast(PlaceDetailView): + def get_queryset(self): + self.place = get_object_or_404(Place, pk=self.kwargs["pk"]) + self.past = True + return ( + get_event_qs(self.request) + .filter(exact_location=self.place) + .filter( + Q(other_versions__isnull=True) + | Q(other_versions__representative=F("pk")) + | Q(other_versions__representative__isnull=True) + ) + .filter(start_day__lte=datetime.now()) + .order_by("-start_day") + ) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["past"] = self.past + return context + + +class UpdatePlaces: + def form_valid(self, form): + result = super().form_valid(form) + p = form.instance + + if not hasattr(self, "nb_applied"): + self.nb_applied = 0 + + # if required, find all matching events + if form.apply(): + self.nb_applied += p.associate_matching_events() + + if self.nb_applied > 1: + messages.success( + self.request, + _("{} events have been updated.").format(self.nb_applied), + ) + elif self.nb_applied == 1: + messages.success(self.request, _("1 event has been updated.")) + else: + messages.info(self.request, _("No events have been modified.")) + return result + + +class PlaceUpdateView( + UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, UpdateView +): + model = Place + permission_required = "agenda_culturel.change_place" + success_message = _("The place has been successfully updated.") + form_class = PlaceForm + + +class PlaceCreateView( + UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, CreateView +): + model = Place + permission_required = "agenda_culturel.add_place" + success_message = _("The place has been successfully created.") + form_class = PlaceForm + + +class PlaceDeleteView(PermissionRequiredMixin, DeleteView): + model = Place + permission_required = "agenda_culturel.delete_place" + success_url = reverse_lazy("view_places_admin") + + +class UnknownPlacesListView(PermissionRequiredMixin, ListView): + model = Event + permission_required = "agenda_culturel.add_place" + paginate_by = 10 + ordering = ["-pk"] + template_name = "agenda_culturel/place_unknown_list.html" + queryset = Event.get_qs_events_with_unkwnon_place() + + +def fix_unknown_places(request): + # get all places + places = Place.objects.all() + # get all events without exact location + u_events = Event.get_qs_events_with_unkwnon_place() + + to_be_updated = [] + # try to find matches + for ue in u_events: + for p in places: + if p.match(ue): + ue.exact_location = p + to_be_updated.append(ue) + continue + # update events with a location + Event.objects.bulk_update(to_be_updated, fields=["exact_location"]) + + # create a success message + nb = len(to_be_updated) + if nb > 1: + messages.success(request, _("{} events have been updated.").format(nb)) + elif nb == 1: + messages.success(request, _("1 event has been updated.")) + else: + messages.info(request, _("No events have been modified.")) + + # come back to the list of places + return HttpResponseRedirect(reverse_lazy("view_unknown_places")) + + +class UnknownPlaceAddView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView): + model = Event + permission_required = ( + "agenda_culturel.change_place", + "agenda_culturel.change_event", + ) + form_class = EventAddPlaceForm + template_name = "agenda_culturel/place_unknown_form.html" + + def form_valid(self, form): + self.modified_event = form.cleaned_data.get("place") + self.add_alias = form.cleaned_data.get("add_alias") + result = super().form_valid(form) + + if form.cleaned_data.get("place"): + messages.success( + self.request, + _("The selected place has been assigned to the event."), + ) + if form.cleaned_data.get("add_alias"): + messages.success( + self.request, + _("A new alias has been added to the selected place."), + ) + + nb_applied = form.cleaned_data.get("place").associate_matching_events() + + if nb_applied > 1: + messages.success( + self.request, + _("{} events have been updated.").format(nb_applied), + ) + elif nb_applied == 1: + messages.success(self.request, _("1 event has been updated.")) + else: + messages.info(self.request, _("No events have been modified.")) + + return result + + def get_success_url(self): + if self.modified_event: + return reverse_lazy("view_unknown_places") + else: + param = "?add=1" if self.add_alias else "" + return reverse_lazy("add_place_from_event", args=[self.object.pk]) + param + + +class PlaceFromEventCreateView(PlaceCreateView): + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["event"] = self.event + return context + + def get_initial(self, *args, **kwargs): + initial = super().get_initial(**kwargs) + self.event = get_object_or_404(Event, pk=self.kwargs["pk"]) + if self.event.location and "add" in self.request.GET: + initial["aliases"] = [self.event.location] + guesser = PlaceGuesser() + name, address, postcode, city = guesser.guess_address_elements( + self.event.location + ) + initial["name"] = name + initial["address"] = address + initial["postcode"] = postcode + initial["city"] = city + initial["location"] = "" + + return initial + + def form_valid(self, form): + result = super().form_valid(form) + self.event.exact_location = form.instance + self.event.save(update_fields=["exact_location"]) + return result + + def get_success_url(self): + return self.event.get_absolute_url()