WIP: interface de saisie de la position par une carte (trop complexe, abandonnée

pour l'instant)
This commit is contained in:
Jean-Marie Favreau 2024-10-16 12:09:23 +02:00
parent fba52afbb0
commit 3fbae56cbf
4 changed files with 91 additions and 23 deletions

View File

@ -246,7 +246,7 @@ class Place(models.Model):
null=True, null=True,
) )
city = models.CharField(verbose_name=_("City"), help_text=_("City name")) city = models.CharField(verbose_name=_("City"), help_text=_("City name"))
location = LocationField(based_fields=["name", "address", "city"], zoom=12, default=Point(3.08333, 45.783329)) location = LocationField(based_fields=["name", "address", "city"], zoom=12, default=Point(3.08333, 45.783329), srid=4326)
aliases = ArrayField( aliases = ArrayField(
models.CharField(max_length=512), models.CharField(max_length=512),

View File

@ -130,6 +130,10 @@ details[role="list"] summary + ul li.selected>a:hover {
display: none; display: none;
} }
} }
// on les cache pour le cas il n'y a pas de js
.nojs, .hidden {
display: none;
}
} }
@media (min-width: 992px) { @media (min-width: 992px) {
@ -325,7 +329,7 @@ footer [data-tooltip] {
text-align: center; text-align: center;
position: fixed; position: fixed;
top: 65vh; top: 65vh;
z-index: 10; z-index: 1000;
opacity: 0.9; opacity: 0.9;
a { a {
color: var(--primary-inverse); color: var(--primary-inverse);
@ -1249,4 +1253,4 @@ form.messages div {
display: inline-block; display: inline-block;
margin-right: 1em; margin-right: 1em;
} }
} }

View File

@ -1,6 +1,7 @@
{% load cat_extra %} {% load cat_extra %}
{% load tag_extra %} {% load tag_extra %}
{% load utils_extra %} {% load utils_extra %}
{% load static %}
{% if not noarticle %} {% if not noarticle %}
<article id="filters"> <article id="filters">
@ -23,9 +24,9 @@
{% for s in filter.get_status_names %} {% for s in filter.get_status_names %}
{{ s }} {{ s }}
{% endfor %} {% endfor %}
{% for c in filter.get_cities %} {% if filter.get_position %}
{{ c }} <span data-tooltip="{{ filter.get_position }}">{% picto_from_name "map-pin" %}</span>
{% endfor %} {% endif %}
{{ filter.get_recurrence_filtering }} {{ filter.get_recurrence_filtering }}
{% else %} {% else %}
Autres filtres Autres filtres
@ -37,8 +38,47 @@
</summary> </summary>
<form method="get" class="form django-form main-filter"> <form method="get" class="form django-form main-filter">
{{ filter.form.as_p }} <div class="map-widget">
<button type="submit">Appliquer le filtre</button> <div id="map_position_map" style="width: 100%; height: 50vh;" class="nojs"></div>
{% for f in filter.form.visible_fields %}
{% if f.id_for_label and f.id_for_label in "id_position_filter,id_position_map,id_position_gps" %}
<div {% if f.id_for_label == "id_position_map" %}
class="hidden"
{% else %}
{% if f.id_for_label == "id_position_gps" %}
class="nojs"
{% else %}
class="withjs"
{% endif %}
{% endif %}
>
{{ f.errors }}
{{ f.label_tag }} {{ f }}
<span class="helptext">{{ f.help_text }}</span>
</div>
{% endif %}
{% endfor %}
</div>
{{ form.media }}
{% for f in filter.form.visible_fields %}
{% if not f.id_for_label or not f.id_for_label in "id_position_filter,id_position_map,id_position_gps" %}
<p>
{{ f.errors }}
{{ f.label_tag }} {{ f }}
<span class="helptext">{{ f.help_text }}</span>
</p>
{% endif %}
{% endfor %}
<button type="submit">Appliquer le filtre</button>
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script src="/static/admin/js/jquery.init.js"></script>
<script src="/static/location_field/js/form.js"></script>
<script>
document.querySelectorAll('.nojs').forEach(function(item) {
item.classList.remove('nojs'); });
document.querySelectorAll('.withjs').forEach(function(item) {
item.classList.add('nojs'); });
</script>
</form> </form>
</details> </details>
<div class="clear"></div> <div class="clear"></div>

View File

@ -11,10 +11,15 @@ from django import forms
from django.contrib.postgres.search import SearchQuery, SearchHeadline from django.contrib.postgres.search import SearchQuery, SearchHeadline
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse from django.urls import reverse
from collections import Counter from collections import Counter
from location_field.widgets import LocationWidget
from django.forms import formset_factory from django.forms import formset_factory
from .forms import ( from .forms import (
@ -32,7 +37,8 @@ from .forms import (
CategorisationForm, CategorisationForm,
EventAddPlaceForm, EventAddPlaceForm,
PlaceForm, PlaceForm,
MultipleHiddenInput MultipleHiddenInput,
HiddenInput
) )
from .models import ( from .models import (
@ -170,6 +176,21 @@ class EventFilter(django_filters.FilterSet):
("only_recurrent", "Montrer uniquement les événements récurrents"), ("only_recurrent", "Montrer uniquement les événements récurrents"),
] ]
position_filter = django_filters.CharFilter(
label="Filtrer par position",
method="filter_position",
help_text="Format: latitude,longitude,distance (km). Exemple: 45.783329,3.08333,3"
)
position_gps = django_filters.CharFilter(
label="Recherche par adresse",
)
position_map = django_filters.CharFilter(
label="Position sur la carte",
widget=LocationWidget(based_fields=['position_gps'])
)
exclude_tags = django_filters.MultipleChoiceFilter( exclude_tags = django_filters.MultipleChoiceFilter(
label="Exclure les étiquettes", label="Exclure les étiquettes",
choices=[(t, t) for t in Event.get_all_tags()], choices=[(t, t) for t in Event.get_all_tags()],
@ -201,13 +222,6 @@ class EventFilter(django_filters.FilterSet):
widget=MultipleHiddenInput, widget=MultipleHiddenInput,
) )
city = django_filters.MultipleChoiceFilter(
label="Filtrer par ville",
field_name="exact_location__city",
choices=[(c, c) for c in Place.get_all_cities()],
widget=forms.CheckboxSelectMultiple,
)
status = django_filters.MultipleChoiceFilter( status = django_filters.MultipleChoiceFilter(
label="Filtrer par status", label="Filtrer par status",
choices=Event.STATUS.choices, choices=Event.STATUS.choices,
@ -217,13 +231,22 @@ class EventFilter(django_filters.FilterSet):
class Meta: class Meta:
model = Event model = Event
fields = ["category", "city", "tags", "exclude_tags", "status", "recurrences"] fields = ["category", "tags", "exclude_tags", "status", "recurrences"]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if not kwargs["request"].user.is_authenticated: if not kwargs["request"].user.is_authenticated:
self.form.fields.pop("status") self.form.fields.pop("status")
def filter_position(self, queryset, name, value):
values = value.split(',')
if len(values) != 3:
return queryset
else:
p = Point(float(values[1]), float(values[0]), srid=4326)
d = float(values[2])
return queryset.exclude(exact_location=False).filter(exact_location__location__distance_lt=(p, D(km=d)))
def filter_recurrences(self, queryset, name, value): def filter_recurrences(self, queryset, name, value):
# construct the full lookup expression # construct the full lookup expression
lookup = "__".join([name, "isnull"]) lookup = "__".join([name, "isnull"])
@ -296,12 +319,13 @@ class EventFilter(django_filters.FilterSet):
def get_status(self): def get_status(self):
return self.get_cleaned_data("status") return self.get_cleaned_data("status")
def get_cities(self): def get_position(self):
return self.get_cleaned_data("city") return self.get_cleaned_data("position_filter")
def to_str(self, prefix=''): def to_str(self, prefix=''):
self.form.full_clean() self.form.full_clean()
result = ' '.join([c.name for c in self.get_categories()] + [t for t in self.get_tags()] + [c for c in self.get_cities()]) result = ' '.join([c.name for c in self.get_categories()] + [t for t in self.get_tags()] + [t for t in self.get_exclude_tags()] + [p for p in self.get_position()])
if len(result) > 0: if len(result) > 0:
result = prefix + result result = prefix + result
return result return result
@ -346,7 +370,7 @@ class EventFilter(django_filters.FilterSet):
len(self.get_cleaned_data("tags")) != 0 len(self.get_cleaned_data("tags")) != 0
or len(self.get_cleaned_data("exclude_tags")) != 0 or len(self.get_cleaned_data("exclude_tags")) != 0
or len(self.get_cleaned_data("recurrences")) != 0 or len(self.get_cleaned_data("recurrences")) != 0
or len(self.get_cleaned_data("city")) != 0 or len(self.get_cleaned_data("position_filter")) != 0
) )
def is_active(self, only_categories=False): def is_active(self, only_categories=False):
@ -361,7 +385,7 @@ class EventFilter(django_filters.FilterSet):
len(self.get_cleaned_data("tags")) != 0 len(self.get_cleaned_data("tags")) != 0
or len(self.get_cleaned_data("exclude_tags")) != 0 or len(self.get_cleaned_data("exclude_tags")) != 0
or len(self.get_cleaned_data("recurrences")) != 0 or len(self.get_cleaned_data("recurrences")) != 0
or len(self.get_cleaned_data("city")) != 0 or len(self.get_cleaned_data("position_filter")) != 0
) )
def is_selected(self, cat): def is_selected(self, cat):
@ -1889,7 +1913,7 @@ class PlaceUpdateView(
class PlaceCreateView( class PlaceCreateView(
UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, CreateView UpdatePlaces, SuccessMessageMixin, CreateView
): ):
model = Place model = Place
permission_required = "agenda_culturel.add_place" permission_required = "agenda_culturel.add_place"