From 91907be9840d3c28444f02f9fce10c5fd6881440 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 18:10:11 +0100 Subject: [PATCH] Suggestions pour les champs d'un nouveau lieu Voir #231 --- src/agenda_culturel/forms.py | 13 +- .../templates/agenda_culturel/place_form.html | 1 + src/agenda_culturel/utils.py | 114 ++++++++++++++++++ src/agenda_culturel/views.py | 15 +++ 4 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/agenda_culturel/utils.py diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index cd7ced7..e121ef9 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -16,6 +16,7 @@ from django.forms import ( ) from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget +from .utils import PlaceGuesser from .models import ( Event, RecurrentImport, @@ -741,13 +742,17 @@ class PlaceForm(ModelForm): fields = "__all__" widgets = {"location": TextInput()} + def __init__(self, *args, **kwargs): + self.force_adjust = kwargs.pop("force_adjust", None) + super().__init__(*args, **kwargs) + def as_grid(self): - return mark_safe( - '
' + result = ('
' + super().as_p() + '
' - + '

Cliquez pour ajuster la position GPS

' - ) + + '

Cliquez pour ajuster la position GPS

') + + return mark_safe(result) def apply(self): return self.cleaned_data.get("apply_to_all") diff --git a/src/agenda_culturel/templates/agenda_culturel/place_form.html b/src/agenda_culturel/templates/agenda_culturel/place_form.html index f7bc4dc..c06d41b 100644 --- a/src/agenda_culturel/templates/agenda_culturel/place_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/place_form.html @@ -22,6 +22,7 @@
{% if event %}

Création d'un lieu depuis l'événement « {{ event }} » (voir en bas de page le détail de l'événement).

+

Remarque : les champs ont été pré-remplis à partir de la description sous forme libre et n'est probablement pas parfaite.

{% endif %}
{% csrf_token %} {{ form.as_grid }} diff --git a/src/agenda_culturel/utils.py b/src/agenda_culturel/utils.py new file mode 100644 index 0000000..cf5b303 --- /dev/null +++ b/src/agenda_culturel/utils.py @@ -0,0 +1,114 @@ +from agenda_culturel.models import ReferenceLocation +import re +import unicodedata + + +class PlaceGuesser: + + def __init__(self): + self.__citynames = list(ReferenceLocation.objects.values_list("name__lower__unaccent", "name")) + [("clermont-fd", "Clermont-Ferrand"), ("aurillac", "Aurillac"), ("montlucon", "Montluçon"), ("montferrand", "Clermont-Ferrand")] + self.__citynames = [(x[0].replace("-", " "), x[1]) for x in self.__citynames] + + def __remove_accents(self, input_str): + if input_str is None: + return None + nfkd_form = unicodedata.normalize("NFKD", input_str) + return "".join([c for c in nfkd_form if not unicodedata.combining(c)]) + + + def __guess_is_address(self, part): + toponyms = ["bd", "rue", "avenue", "place", "boulevard", "allee", ] + part = part.strip() + if re.match(r'^[0-9]', part): + return True + + elems = part.split(" ") + return any([self.__remove_accents(e.lower()) in toponyms for e in elems]) + + + def __clean_address(self, addr): + toponyms = ["bd", "rue", "avenue", "place", "boulevard", "allée", "bis", "ter", "ZI"] + for t in toponyms: + addr = re.sub(" " + t + " ", " " + t + " ", addr, flags=re.IGNORECASE) + return addr + + def __guess_city_name(self, part): + part = part.strip().replace(" - ", "-") + if len(part) == 0: + return None + part = self.__remove_accents(part.lower()).replace("-", " ") + match = [x[1] for x in self.__citynames if x[0] == part] + if len(match) > 0: + return match[0] + else: + return None + + def __guess_city_name_postcode(self, part): + with_pc = re.search(r'^(.*)(([0-9][ ]*){5})(.*)$', part) + if with_pc: + p1 = self.__guess_city_name(with_pc.group(1).strip()) + postcode = with_pc.group(2).replace(" ", "") + p2 = self.__guess_city_name(with_pc.group(4).strip()) + return postcode, p2, p1 + else: + return None, self.__guess_city_name(part), None + + def __guess_name_address(self, part): + with_num = re.search(r'^(([^0-9])+)([0-9]+)(.*)', part) + if with_num: + name = with_num.group(1) + return name, part[len(name):] + else: + return "", part + + def guess_address_elements(self, alias): + parts = re.split(r'[,/à]', alias) + parts = [p1 for p1 in [p.strip() for p in parts] if p1 != "" and p1.lower() != "france"] + + name = "" + address = "" + postcode = "" + city = "" + possible_city = "" + + oparts = [] + for part in parts: + p, c, possible_c = self.__guess_city_name_postcode(part) + if not possible_c is None: + possible_city = possible_c + if not c is None and city == "": + city = c + if not p is None and postcode == "": + postcode = p + if p is None and c is None: + oparts.append(part) + + if city == "" and possible_city != "": + city = possible_city + else: + if len(oparts) == 0 and not possible_city != "": + oparts = [possible_city] + + if city == "": + alias_simple = self.__remove_accents(alias.lower()).replace("-", " ") + mc = [x[1] for x in self.__citynames if alias_simple.endswith(" " + x[0])] + if len(mc) == 1: + city = mc[0] + + + + + if len(oparts) > 0: + if not self.__guess_is_address(oparts[0]): + name = oparts[0] + address = ", ".join(oparts[1:]) + else: + name, address = self.__guess_name_address(", ".join(oparts)) + + address = self.__clean_address(address) + + if name == "" and possible_city != "" and possible_city != city: + name = possible_city + + return name, address, postcode, city + diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index 5022f28..46b7f77 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -12,6 +12,7 @@ from django.contrib.postgres.search import SearchQuery, SearchHeadline from django.utils.safestring import mark_safe from django.utils.decorators import method_decorator from honeypot.decorators import check_honeypot +from .utils import PlaceGuesser from django.contrib.gis.geos import Point @@ -1864,16 +1865,30 @@ class UnknownPlaceAddView(PermissionRequiredMixin, SuccessMessageMixin, UpdateVi class PlaceFromEventCreateView(PlaceCreateView): + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["event"] = self.event return context + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + if self.event.location and "add" in self.request.GET: + kwargs["force_adjust"] = True + return kwargs + + 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["city"] = city + return initial def form_valid(self, form):