diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py
index 6bb1249..d26aba3 100644
--- a/src/agenda_culturel/forms.py
+++ b/src/agenda_culturel/forms.py
@@ -1,8 +1,8 @@
-from django.forms import ModelForm, ValidationError, TextInput, Form, URLField, MultipleHiddenInput, Textarea, CharField, ChoiceField, RadioSelect, MultipleChoiceField, BooleanField, HiddenInput
+from django.forms import ModelForm, ValidationError, TextInput, Form, URLField, MultipleHiddenInput, Textarea, CharField, ChoiceField, RadioSelect, MultipleChoiceField, BooleanField, HiddenInput, ModelChoiceField
from datetime import date
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
-from .models import Event, BatchImportation, RecurrentImport, CategorisationRule, ModerationAnswer, ModerationQuestion
+from .models import Event, BatchImportation, RecurrentImport, CategorisationRule, ModerationAnswer, ModerationQuestion, Place
from django.utils.translation import gettext_lazy as _
from string import ascii_uppercase as auc
from .templatetags.utils_extra import int_to_abc
@@ -283,8 +283,6 @@ class CategorisationForm(Form):
if '_' not in f:
if f + '_cat' in args[0]:
events.append((Event.objects.get(pk=int(f)), args[0][f + '_cat']))
-
- # TODO
super().__init__(*args, **kwargs)
for e, c in events:
@@ -294,3 +292,48 @@ class CategorisationForm(Form):
def get_validated(self):
return [(e, self.cleaned_data.get(e + '_cat')) for e in self.fields if '_' not in e and self.cleaned_data.get(e)]
+
+class EventAddPlaceForm(Form):
+
+ place = ModelChoiceField(label=_("Place"), queryset=Place.objects.all(), empty_label=_("Create a missing place"), required=False)
+ add_alias = BooleanField(initial=True, required=False)
+
+ def __init__(self, *args, **kwargs):
+ self.instance = kwargs.pop('instance', False)
+ super().__init__(*args, **kwargs)
+ if self.instance.location:
+ self.fields["add_alias"].label = _("Add \"{}\" to the aliases of the place").format(self.instance.location)
+ else:
+ self.fields.pop("add_alias")
+
+ def modified_event(self):
+ return self.cleaned_data.get('place')
+
+ def save(self):
+ if self.cleaned_data.get("place"):
+ place = self.cleaned_data.get("place")
+ self.instance.exact_location = place
+ self.instance.save()
+ if self.cleaned_data.get('add_alias'):
+ place.aliases.append(self.instance.location)
+ place.save()
+
+ return self.instance
+
+class PlaceForm(ModelForm):
+ apply_to_all = BooleanField(initial=True, label=_('On saving, use aliases to detect all matching events with missing place'), required=False)
+
+ class Meta:
+ model = Place
+ fields = '__all__'
+ widgets = {
+ 'location': TextInput()
+ }
+
+ def as_grid(self):
+ return mark_safe('
' + super().as_p() + '
' +
+ '
')
+
+
+ def apply(self):
+ return self.cleaned_data.get("apply_to_all")
diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po
index 785f14b..f2eaabf 100644
--- a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po
+++ b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: agenda_culturel\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-26 14:59+0000\n"
+"POT-Creation-Date: 2024-04-27 16:25+0000\n"
"PO-Revision-Date: 2023-10-29 14:16+0000\n"
"Last-Translator: Jean-Marie Favreau \n"
"Language-Team: Jean-Marie Favreau \n"
@@ -29,19 +29,35 @@ msgstr "L'heure de fin ne peut pas être avant l'heure de début."
msgid "JSON in the format expected for the import."
msgstr "JSON dans le format attendu pour l'import"
-#: agenda_culturel/forms.py:291
-#, fuzzy
-#| msgid "Category of the event"
+#: agenda_culturel/forms.py:289
msgid "Apply category {} to the event {}"
-msgstr "Catégorie de l'événement"
+msgstr "Appliquer la catégorie {} à l'événement {}"
+
+#: agenda_culturel/forms.py:298 agenda_culturel/models.py:170
+msgid "Place"
+msgstr "Lieu"
+
+#: agenda_culturel/forms.py:298
+msgid "Create a missing place"
+msgstr "Créer un lieu manquant"
+
+#: agenda_culturel/forms.py:305
+msgid "Add \"{}\" to the aliases of the place"
+msgstr "Ajouter « {} » aux alias du lieu"
+
+#: agenda_culturel/forms.py:324
+msgid "On saving, use aliases to detect all matching events with missing place"
+msgstr ""
+"Lors de l'enregistrement, utiliser des alias pour détecter tous les "
+"événements correspondants dont la place est manquante."
#: agenda_culturel/import_tasks/generic_extractors.py:151
msgid "Cannot extract event from url {}"
msgstr "Impossible d'extraire l'événement depuis l'url {}"
#: agenda_culturel/models.py:39 agenda_culturel/models.py:68
-#: agenda_culturel/models.py:162 agenda_culturel/models.py:753
-#: agenda_culturel/models.py:792
+#: agenda_culturel/models.py:162 agenda_culturel/models.py:770
+#: agenda_culturel/models.py:809
msgid "Name"
msgstr "Nom"
@@ -89,8 +105,8 @@ msgstr "Couleur"
msgid "Color used as background for the category"
msgstr "Couleur utilisée comme fond de la catégorie"
-#: agenda_culturel/models.py:115 agenda_culturel/models.py:195
-#: agenda_culturel/models.py:804 agenda_culturel/models.py:855
+#: agenda_culturel/models.py:115 agenda_culturel/models.py:205
+#: agenda_culturel/models.py:821 agenda_culturel/models.py:872
msgid "Category"
msgstr "Catégorie"
@@ -107,14 +123,22 @@ msgid "Address"
msgstr "Adresse"
#: agenda_culturel/models.py:163
-msgid "Address of this place"
-msgstr "Adresse de ce lieu"
+msgid "Address of this place (without city name)"
+msgstr "Adresse de ce lieu (sans le nom de la ville)"
-#: agenda_culturel/models.py:166
+#: agenda_culturel/models.py:164
+msgid "City"
+msgstr "Ville"
+
+#: agenda_culturel/models.py:164
+msgid "City name"
+msgstr "Nom de la ville"
+
+#: agenda_culturel/models.py:167
msgid "Alternative names"
msgstr "Noms alternatifs"
-#: agenda_culturel/models.py:166
+#: agenda_culturel/models.py:167
msgid ""
"Alternative names or addresses used to match a place with the free-form "
"location of an event."
@@ -122,81 +146,77 @@ msgstr ""
"Noms et adresses alternatives qui seront utilisées pour associer une adresse "
"avec la localisation en forme libre d'un événement"
-#: agenda_culturel/models.py:169
-msgid "Place"
-msgstr "Lieu"
-
-#: agenda_culturel/models.py:170
+#: agenda_culturel/models.py:171
msgid "Places"
msgstr "Lieux"
-#: agenda_culturel/models.py:179 agenda_culturel/models.py:802
+#: agenda_culturel/models.py:189 agenda_culturel/models.py:819
msgid "Published"
msgstr "Publié"
-#: agenda_culturel/models.py:180
+#: agenda_culturel/models.py:190
msgid "Draft"
msgstr "Brouillon"
-#: agenda_culturel/models.py:181
+#: agenda_culturel/models.py:191
msgid "Trash"
msgstr "Corbeille"
-#: agenda_culturel/models.py:191
+#: agenda_culturel/models.py:201
msgid "Title"
msgstr "Titre"
-#: agenda_culturel/models.py:191
+#: agenda_culturel/models.py:201
msgid "Short title"
msgstr "Titre court"
-#: agenda_culturel/models.py:193 agenda_culturel/models.py:839
+#: agenda_culturel/models.py:203 agenda_culturel/models.py:856
msgid "Status"
msgstr "Status"
-#: agenda_culturel/models.py:195
+#: agenda_culturel/models.py:205
msgid "Category of the event"
msgstr "Catégorie de l'événement"
-#: agenda_culturel/models.py:197
+#: agenda_culturel/models.py:207
msgid "Day of the event"
msgstr "Date de l'événement"
-#: agenda_culturel/models.py:198
+#: agenda_culturel/models.py:208
msgid "Starting time"
msgstr "Heure de début"
-#: agenda_culturel/models.py:200
+#: agenda_culturel/models.py:210
msgid "End day of the event"
msgstr "Fin de l'événement"
-#: agenda_culturel/models.py:200
+#: agenda_culturel/models.py:210
msgid "End day of the event, only required if different from the start day."
msgstr ""
"Date de fin de l'événement, uniquement nécessaire s'il est différent du "
"premier jour de l'événement"
-#: agenda_culturel/models.py:201
+#: agenda_culturel/models.py:211
msgid "Final time"
msgstr "Heure de fin"
-#: agenda_culturel/models.py:203
+#: agenda_culturel/models.py:213
msgid "Recurrence"
msgstr "Récurrence"
-#: agenda_culturel/models.py:205 agenda_culturel/models.py:803
+#: agenda_culturel/models.py:215 agenda_culturel/models.py:820
msgid "Location"
msgstr "Localisation"
-#: agenda_culturel/models.py:205
+#: agenda_culturel/models.py:215
msgid "Address of the event"
msgstr "Adresse de l'événement"
-#: agenda_culturel/models.py:206
+#: agenda_culturel/models.py:216
msgid "Location (free form)"
msgstr "Localisation (forme libre)"
-#: agenda_culturel/models.py:206
+#: agenda_culturel/models.py:216
msgid ""
"Address of the event in case its not available in the already known places "
"(free form)"
@@ -204,189 +224,187 @@ msgstr ""
"Addresse d'un événement si elle n'est pas déjà présente dans la liste des "
"lieux disponible (forme libre)"
-#: agenda_culturel/models.py:208
+#: agenda_culturel/models.py:218
msgid "Description"
msgstr "Description"
-#: agenda_culturel/models.py:208
+#: agenda_culturel/models.py:218
msgid "General description of the event"
msgstr "Description générale de l'événement"
-#: agenda_culturel/models.py:210
+#: agenda_culturel/models.py:220
msgid "Illustration (local image)"
msgstr "Illustration (image locale)"
-#: agenda_culturel/models.py:210
+#: agenda_culturel/models.py:220
msgid "Illustration image stored in the agenda server"
msgstr "Image d'illustration stockée sur le serveur de l'agenda"
-#: agenda_culturel/models.py:212
+#: agenda_culturel/models.py:222
msgid "Illustration"
msgstr "Illustration"
-#: agenda_culturel/models.py:212
+#: agenda_culturel/models.py:222
msgid "URL of the illustration image"
msgstr "URL de l'image illustrative"
-#: agenda_culturel/models.py:213
+#: agenda_culturel/models.py:223
msgid "Illustration description"
msgstr "Description de l'illustration"
-#: agenda_culturel/models.py:213
+#: agenda_culturel/models.py:223
msgid "Alternative text used by screen readers for the image"
msgstr "Texte alternatif utiliser par les lecteurs d'écrans pour l'image"
-#: agenda_culturel/models.py:215
+#: agenda_culturel/models.py:225
msgid "Importation source"
msgstr "Source d'importation"
-#: agenda_culturel/models.py:215
+#: agenda_culturel/models.py:225
msgid "Importation source used to detect removed entries."
msgstr "Source d'importation utilisée pour détecter les éléments supprimés/"
-#: agenda_culturel/models.py:216
+#: agenda_culturel/models.py:226
msgid "UUIDs"
msgstr "UUIDs"
-#: agenda_culturel/models.py:216
+#: agenda_culturel/models.py:226
msgid "UUIDs from import to detect duplicated entries."
msgstr "UUIDs utilisés pendant l'import pour détecter les entrées dupliquées"
-#: agenda_culturel/models.py:217
+#: agenda_culturel/models.py:227
msgid "URLs"
msgstr "URLs"
-#: agenda_culturel/models.py:217
+#: agenda_culturel/models.py:227
msgid "List of all the urls where this event can be found."
msgstr "Liste de toutes les urls où l'événement peut être trouvé."
-#: agenda_culturel/models.py:219
+#: agenda_culturel/models.py:229
msgid "Tags"
msgstr "Étiquettes"
-#: agenda_culturel/models.py:219
+#: agenda_culturel/models.py:229
msgid "A list of tags that describe the event."
msgstr "Une liste d'étiquettes décrivant l'événement"
-#: agenda_culturel/models.py:221
+#: agenda_culturel/models.py:231
msgid "Possibly duplicated"
msgstr "Possibles doublons"
-#: agenda_culturel/models.py:262
+#: agenda_culturel/models.py:272
msgid "Event"
msgstr "Événement"
-#: agenda_culturel/models.py:263
+#: agenda_culturel/models.py:273
msgid "Events"
msgstr "Événements"
-#: agenda_culturel/models.py:749
+#: agenda_culturel/models.py:766
msgid "Contact message"
msgstr "Message de contact"
-#: agenda_culturel/models.py:750
-#, fuzzy
-#| msgid "Your message"
+#: agenda_culturel/models.py:767
msgid "Contact messages"
msgstr "Messages de contact"
-#: agenda_culturel/models.py:752
+#: agenda_culturel/models.py:769
msgid "Subject"
msgstr "Sujet"
-#: agenda_culturel/models.py:752
+#: agenda_culturel/models.py:769
msgid "The subject of your message"
msgstr "Sujet de votre message"
-#: agenda_culturel/models.py:753
+#: agenda_culturel/models.py:770
msgid "Your name"
msgstr "Votre nom"
-#: agenda_culturel/models.py:754
+#: agenda_culturel/models.py:771
msgid "Email address"
msgstr "Adresse email"
-#: agenda_culturel/models.py:754
+#: agenda_culturel/models.py:771
msgid "Your email address"
msgstr "Votre adresse email"
-#: agenda_culturel/models.py:755
+#: agenda_culturel/models.py:772
msgid "Message"
msgstr "Message"
-#: agenda_culturel/models.py:755
+#: agenda_culturel/models.py:772
msgid "Your message"
msgstr "Votre message"
-#: agenda_culturel/models.py:759 agenda_culturel/views.py:462
+#: agenda_culturel/models.py:776 agenda_culturel/views.py:470
msgid "Closed"
msgstr "Fermé"
-#: agenda_culturel/models.py:759
+#: agenda_culturel/models.py:776
msgid "this message has been processed and no longer needs to be handled"
msgstr "Ce message a été traité et ne nécessite plus d'être pris en charge"
-#: agenda_culturel/models.py:760
+#: agenda_culturel/models.py:777
msgid "Comments"
msgstr "Commentaires"
-#: agenda_culturel/models.py:760
+#: agenda_culturel/models.py:777
msgid "Comments on the message from the moderation team"
msgstr "Commentaires sur ce message par l'équipe de modération"
-#: agenda_culturel/models.py:770 agenda_culturel/models.py:837
+#: agenda_culturel/models.py:787 agenda_culturel/models.py:854
msgid "Recurrent import"
msgstr "Import récurrent"
-#: agenda_culturel/models.py:771
+#: agenda_culturel/models.py:788
msgid "Recurrent imports"
msgstr "Imports récurrents"
-#: agenda_culturel/models.py:775
+#: agenda_culturel/models.py:792
msgid "ical"
msgstr "ical"
-#: agenda_culturel/models.py:776
+#: agenda_culturel/models.py:793
msgid "ical no busy"
msgstr "ical sans busy"
-#: agenda_culturel/models.py:777
+#: agenda_culturel/models.py:794
msgid "ical no VC"
msgstr "ical sans VC"
-#: agenda_culturel/models.py:778
+#: agenda_culturel/models.py:795
msgid "lacoope.org"
msgstr "lacoope.org"
-#: agenda_culturel/models.py:779
+#: agenda_culturel/models.py:796
msgid "la comédie"
msgstr "la comédie"
-#: agenda_culturel/models.py:780
+#: agenda_culturel/models.py:797
msgid "le fotomat"
msgstr "le fotomat"
-#: agenda_culturel/models.py:781
+#: agenda_culturel/models.py:798
msgid "la puce à loreille"
msgstr "la puce à loreille"
-#: agenda_culturel/models.py:784
+#: agenda_culturel/models.py:801
msgid "simple"
msgstr "simple"
-#: agenda_culturel/models.py:785
+#: agenda_culturel/models.py:802
msgid "Headless Chromium"
msgstr "chromium sans interface"
-#: agenda_culturel/models.py:789
+#: agenda_culturel/models.py:806
msgid "daily"
msgstr "chaque jour"
-#: agenda_culturel/models.py:790
+#: agenda_culturel/models.py:807
msgid "weekly"
msgstr "chaque semaine"
-#: agenda_culturel/models.py:792
+#: agenda_culturel/models.py:809
msgid ""
"Recurrent import name. Be careful to choose a name that is easy to "
"understand, as it will be public and displayed on the sites About page."
@@ -394,127 +412,127 @@ msgstr ""
"Nom de l'import récurrent. Attention à choisir un nom compréhensible, car il "
"sera public, et affiché sur la page à propos du site."
-#: agenda_culturel/models.py:793
+#: agenda_culturel/models.py:810
msgid "Processor"
msgstr "Processeur"
-#: agenda_culturel/models.py:794
+#: agenda_culturel/models.py:811
msgid "Downloader"
msgstr "Téléchargeur"
-#: agenda_culturel/models.py:796
+#: agenda_culturel/models.py:813
msgid "Import recurrence"
msgstr "Récurrence d'import"
-#: agenda_culturel/models.py:799
+#: agenda_culturel/models.py:816
msgid "Source"
msgstr "Source"
-#: agenda_culturel/models.py:799
+#: agenda_culturel/models.py:816
msgid "URL of the source document"
msgstr "URL du document source"
-#: agenda_culturel/models.py:800
+#: agenda_culturel/models.py:817
msgid "Browsable url"
msgstr "URL navigable"
-#: agenda_culturel/models.py:800
+#: agenda_culturel/models.py:817
msgid "URL of the corresponding document that will be shown to visitors."
msgstr "URL correspondant au document et qui sera montrée aux visiteurs"
-#: agenda_culturel/models.py:802
+#: agenda_culturel/models.py:819
msgid "Status of each imported event (published or draft)"
msgstr "Status de chaque événement importé (publié ou brouillon)"
-#: agenda_culturel/models.py:803
+#: agenda_culturel/models.py:820
msgid "Address for each imported event"
msgstr "Adresse de chaque événement importé"
-#: agenda_culturel/models.py:804
+#: agenda_culturel/models.py:821
msgid "Category of each imported event"
msgstr "Catégorie de chaque événement importé"
-#: agenda_culturel/models.py:805
+#: agenda_culturel/models.py:822
msgid "Tags for each imported event"
msgstr "Étiquettes de chaque événement importé"
-#: agenda_culturel/models.py:805
+#: agenda_culturel/models.py:822
msgid "A list of tags that describe each imported event."
msgstr "Une liste d'étiquettes décrivant chaque événement importé"
-#: agenda_culturel/models.py:824
+#: agenda_culturel/models.py:841
msgid "Running"
msgstr "En cours"
-#: agenda_culturel/models.py:825
+#: agenda_culturel/models.py:842
msgid "Canceled"
msgstr "Annulé"
-#: agenda_culturel/models.py:826
+#: agenda_culturel/models.py:843
msgid "Success"
msgstr "Succès"
-#: agenda_culturel/models.py:827
+#: agenda_culturel/models.py:844
msgid "Failed"
msgstr "Erreur"
-#: agenda_culturel/models.py:830
+#: agenda_culturel/models.py:847
msgid "Batch importation"
msgstr "Importation par lot"
-#: agenda_culturel/models.py:831
+#: agenda_culturel/models.py:848
msgid "Batch importations"
msgstr "Importations par lot"
-#: agenda_culturel/models.py:837
+#: agenda_culturel/models.py:854
msgid "Reference to the recurrent import processing"
msgstr "Référence du processus d'import récurrent"
-#: agenda_culturel/models.py:841
+#: agenda_culturel/models.py:858
msgid "Error message"
msgstr "Votre message"
-#: agenda_culturel/models.py:843
+#: agenda_culturel/models.py:860
msgid "Number of collected events"
msgstr "Nombre d'événements collectés"
-#: agenda_culturel/models.py:844
+#: agenda_culturel/models.py:861
msgid "Number of imported events"
msgstr "Nombre d'événements importés"
-#: agenda_culturel/models.py:845
+#: agenda_culturel/models.py:862
msgid "Number of updated events"
msgstr "Nombre d'événements mis à jour"
-#: agenda_culturel/models.py:846
+#: agenda_culturel/models.py:863
msgid "Number of removed events"
msgstr "Nombre d'événements supprimés"
-#: agenda_culturel/models.py:853
+#: agenda_culturel/models.py:870
msgid "Weight"
msgstr "Poids"
-#: agenda_culturel/models.py:853
+#: agenda_culturel/models.py:870
msgid "The lower is the weight, the earlier the filter is applied"
msgstr "Plus le poids est léger, plus le filtre sera appliqué tôt"
-#: agenda_culturel/models.py:855
+#: agenda_culturel/models.py:872
msgid "Category applied to the event"
msgstr "Catégorie appliquée à l'événement"
-#: agenda_culturel/models.py:857
+#: agenda_culturel/models.py:874
msgid "Contained in the title"
msgstr "Contenu dans le titre"
-#: agenda_culturel/models.py:857
+#: agenda_culturel/models.py:874
msgid "Text contained in the event title"
msgstr "Texte contenu dans le titre de l'événement"
-#: agenda_culturel/models.py:858
+#: agenda_culturel/models.py:875
msgid "Exact title extract"
msgstr "Extrait exact du titre"
-#: agenda_culturel/models.py:858
+#: agenda_culturel/models.py:875
msgid ""
"If checked, the extract will be searched for in the title using the exact "
"form (capitals, accents)."
@@ -522,19 +540,19 @@ msgstr ""
"Si coché, l'extrait sera recherché dans le titre en utilisant la forme "
"exacte (majuscules, accents)"
-#: agenda_culturel/models.py:860
+#: agenda_culturel/models.py:877
msgid "Contained in the description"
msgstr "Contenu dans la description"
-#: agenda_culturel/models.py:860
+#: agenda_culturel/models.py:877
msgid "Text contained in the description"
msgstr "Texte contenu dans la description"
-#: agenda_culturel/models.py:861
+#: agenda_culturel/models.py:878
msgid "Exact description extract"
msgstr "Extrait exact de description"
-#: agenda_culturel/models.py:861
+#: agenda_culturel/models.py:878
msgid ""
"If checked, the extract will be searched for in the description using the "
"exact form (capitals, accents)."
@@ -542,19 +560,19 @@ msgstr ""
"Si coché, l'extrait sera recherché dans la description en utilisant la forme "
"exacte (majuscules, accents)"
-#: agenda_culturel/models.py:863
+#: agenda_culturel/models.py:880
msgid "Contained in the location"
msgstr "Contenu dans la localisation"
-#: agenda_culturel/models.py:863
+#: agenda_culturel/models.py:880
msgid "Text contained in the event location"
msgstr "Texte contenu dans la localisation de l'événement"
-#: agenda_culturel/models.py:864
+#: agenda_culturel/models.py:881
msgid "Exact location extract"
msgstr "Extrait exact de localisation"
-#: agenda_culturel/models.py:864
+#: agenda_culturel/models.py:881
msgid ""
"If checked, the extract will be searched for in the location using the exact "
"form (capitals, accents)."
@@ -562,52 +580,52 @@ msgstr ""
"Si coché, l'extrait sera recherché dans la localisation en utilisant la "
"forme exacte (majuscules, accents)"
-#: agenda_culturel/models.py:867
+#: agenda_culturel/models.py:884
msgid "Categorisation rule"
msgstr "Règle de catégorisation"
-#: agenda_culturel/models.py:868
+#: agenda_culturel/models.py:885
msgid "Categorisation rules"
msgstr "Règles de catégorisation"
-#: agenda_culturel/models.py:924 agenda_culturel/models.py:945
+#: agenda_culturel/models.py:941 agenda_culturel/models.py:962
msgid "Question"
msgstr "Question"
-#: agenda_culturel/models.py:924 agenda_culturel/models.py:947
+#: agenda_culturel/models.py:941 agenda_culturel/models.py:964
msgid "Text that will be shown to moderators"
msgstr "Text tel que présenté aux modérateurices"
-#: agenda_culturel/models.py:927
+#: agenda_culturel/models.py:944
msgid "Moderation question"
msgstr "Question de modération"
-#: agenda_culturel/models.py:928
+#: agenda_culturel/models.py:945
msgid "Moderation questions"
msgstr "Questions de modération"
-#: agenda_culturel/models.py:945
+#: agenda_culturel/models.py:962
msgid "Associated question from moderation"
msgstr "Question associée pour la modération"
-#: agenda_culturel/models.py:947
+#: agenda_culturel/models.py:964
msgid "Answer"
msgstr "Réponse"
-#: agenda_culturel/models.py:949
+#: agenda_culturel/models.py:966
msgid "Adds tags"
msgstr "Ajoute les étiquettes"
-#: agenda_culturel/models.py:949
+#: agenda_culturel/models.py:966
msgid "A list of tags that will be added if you choose this answer."
msgstr ""
"Une liste d'étiquettes qui seront ajoutées si vous choisissez cette réponse."
-#: agenda_culturel/models.py:950
+#: agenda_culturel/models.py:967
msgid "Removes tags"
msgstr "Retire les étiquettes"
-#: agenda_culturel/models.py:950
+#: agenda_culturel/models.py:967
msgid "A list of tags that will be removed if you choose this answer."
msgstr ""
"Une liste d'étiquettes qui seront retirées si vous choisissez cette réponse."
@@ -620,27 +638,27 @@ msgstr "anglais"
msgid "French"
msgstr "français"
-#: agenda_culturel/views.py:261
+#: agenda_culturel/views.py:269
msgid "The static content has been successfully updated."
msgstr "Le contenu statique a été modifié avec succès."
-#: agenda_culturel/views.py:268 agenda_culturel/views.py:303
+#: agenda_culturel/views.py:276 agenda_culturel/views.py:311
msgid "The event has been successfully modified."
msgstr "L'événement a été modifié avec succès."
-#: agenda_culturel/views.py:280
+#: agenda_culturel/views.py:288
msgid "The event has been successfully deleted."
msgstr "L'événement a été supprimé avec succès"
-#: agenda_culturel/views.py:329
+#: agenda_culturel/views.py:337
msgid "The status has been successfully modified."
msgstr "Le status a été modifié avec succès."
-#: agenda_culturel/views.py:351
+#: agenda_culturel/views.py:359
msgid "The event is saved."
msgstr "L'événement est enregistré."
-#: agenda_culturel/views.py:354
+#: agenda_culturel/views.py:362
msgid ""
"The event has been submitted and will be published as soon as it has been "
"validated by the moderation team."
@@ -648,7 +666,7 @@ msgstr ""
"L'événement a été soumis et sera publié dès qu'il aura été validé par "
"l'équipe de modération."
-#: agenda_culturel/views.py:391
+#: agenda_culturel/views.py:399
msgid ""
"The event has been successfully extracted, and you can now submit it after "
"modifying it if necessary."
@@ -656,7 +674,7 @@ msgstr ""
"L'événement a été extrait avec succès, vous pouvez maintenant le soumettre "
"après l'avoir modifié au besoin."
-#: agenda_culturel/views.py:395
+#: agenda_culturel/views.py:403
msgid ""
"Unable to extract an event from the proposed URL. Please use the form below "
"to submit the event."
@@ -664,16 +682,16 @@ msgstr ""
"Impossible d'extraire un événement depuis l'URL proposée. Veuillez utiliser "
"le formulaire ci-dessous pour soumettre l'événement."
-#: agenda_culturel/views.py:404
+#: agenda_culturel/views.py:412
msgid "This URL has already been submitted, and you can find the event below."
msgstr ""
"Cette URL a déjà été soumise, et vous trouverez l'événement ci-dessous."
-#: agenda_culturel/views.py:408
+#: agenda_culturel/views.py:416
msgid "This URL has already been submitted and is awaiting moderation."
msgstr "Cette URL a déjà été soumise, et est en attente de modération"
-#: agenda_culturel/views.py:410
+#: agenda_culturel/views.py:418
msgid ""
"This URL has already been submitted, but has not been selected for "
"publication by the moderation team."
@@ -681,51 +699,51 @@ msgstr ""
"Cette URL a déjà été soumise, mais n'a pas été retenue par l'équipe de "
"modération pour la publication."
-#: agenda_culturel/views.py:432
+#: agenda_culturel/views.py:440
msgid "Your message has been sent successfully."
msgstr "Votre message a été envoyé avec succès."
-#: agenda_culturel/views.py:447
+#: agenda_culturel/views.py:455
msgid "The contact message properties has been successfully modified."
msgstr "Les propriétés du message de contact ont été modifié avec succès."
-#: agenda_culturel/views.py:462
+#: agenda_culturel/views.py:470
msgid "Open"
msgstr "Ouvert"
-#: agenda_culturel/views.py:504
+#: agenda_culturel/views.py:512
msgid "Search"
msgstr "Rechercher"
-#: agenda_culturel/views.py:639
+#: agenda_culturel/views.py:647
msgid "The import has been run successfully."
msgstr "L'import a été lancé avec succès"
-#: agenda_culturel/views.py:656
+#: agenda_culturel/views.py:664
msgid "The import has been canceled."
msgstr "L'import a été annulé"
-#: agenda_culturel/views.py:693
+#: agenda_culturel/views.py:701
msgid "The recurrent import has been successfully modified."
msgstr "L'import récurrent a été modifié avec succès."
-#: agenda_culturel/views.py:700
+#: agenda_culturel/views.py:708
msgid "The recurrent import has been successfully deleted."
msgstr "L'import récurrent a été supprimé avec succès"
-#: agenda_culturel/views.py:731
+#: agenda_culturel/views.py:739
msgid "The import has been launched."
msgstr "L'import a été lancé"
-#: agenda_culturel/views.py:791
+#: agenda_culturel/views.py:799
msgid "The merge has been successfully completed."
msgstr "La fusion a été réalisée avec succès."
-#: agenda_culturel/views.py:821
+#: agenda_culturel/views.py:829
msgid "Events have been marked as unduplicated."
msgstr "Les événements ont été marqués comme non dupliqués."
-#: agenda_culturel/views.py:838
+#: agenda_culturel/views.py:846
msgid ""
"The selected event has been retained, while the other has been moved to the "
"recycle bin."
@@ -733,7 +751,7 @@ msgstr ""
"L'événement sélectionné a été conservé, l'autre a été déplacé dans la "
"corbeille."
-#: agenda_culturel/views.py:840
+#: agenda_culturel/views.py:848
msgid ""
"The selected event has been retained, while the others have been moved to "
"the recycle bin."
@@ -741,15 +759,15 @@ msgstr ""
"L'événement sélectionné a été conservé, les autres ont été déplacés dans la "
"corbeille."
-#: agenda_culturel/views.py:846
+#: agenda_culturel/views.py:854
msgid "The event has been withdrawn from the group and made independent."
msgstr "L'événement a été retiré du groupe et rendu indépendant."
-#: agenda_culturel/views.py:893
+#: agenda_culturel/views.py:901
msgid "The event was successfully duplicated."
msgstr "L'événement a été marqué dupliqué avec succès."
-#: agenda_culturel/views.py:896
+#: agenda_culturel/views.py:904
msgid ""
"The event has been successfully flagged as a duplicate. The moderation team "
"will deal with your suggestion shortly."
@@ -757,35 +775,63 @@ msgstr ""
"L'événement a été signalé comme dupliqué avec succès. Votre suggestion sera "
"prochainement prise en charge par l'équipe de modération."
-#: agenda_culturel/views.py:935
+#: agenda_culturel/views.py:943
msgid "The categorisation rule has been successfully modified."
msgstr "La règle de catégorisation a été modifiée avec succès."
-#: agenda_culturel/views.py:942
+#: agenda_culturel/views.py:950
msgid "The categorisation rule has been successfully deleted."
msgstr "La règle de catégorisation a été supprimée avec succès"
-#: agenda_culturel/views.py:962 agenda_culturel/views.py:991
+#: agenda_culturel/views.py:970 agenda_culturel/views.py:999
msgid "The rules were successfully applied and 1 event was categorised."
msgstr ""
"Les règles ont été appliquées avec succès et 1 événement a été catégorisé"
-#: agenda_culturel/views.py:964 agenda_culturel/views.py:993
+#: agenda_culturel/views.py:972 agenda_culturel/views.py:1001
msgid "The rules were successfully applied and {} events were categorised."
msgstr ""
"Les règles ont été appliquées avec succès et {} événements ont été "
"catégorisés"
-#: agenda_culturel/views.py:966 agenda_culturel/views.py:995
+#: agenda_culturel/views.py:974 agenda_culturel/views.py:1003
msgid "The rules were successfully applied and no events were categorised."
msgstr ""
"Les règles ont été appliquées avec succès et aucun événement n'a été "
"catégorisé"
-#: agenda_culturel/views.py:1023
+#: agenda_culturel/views.py:1031
msgid "The moderation question has been created with success."
msgstr "La question de modération a été créée avec succès."
+#: agenda_culturel/views.py:1123 agenda_culturel/views.py:1178
+msgid "{} events have been updated."
+msgstr "{} événements ont été mis à jour."
+
+#: agenda_culturel/views.py:1125 agenda_culturel/views.py:1180
+msgid "1 event has been updated."
+msgstr "1 événement a été mis à jour"
+
+#: agenda_culturel/views.py:1127 agenda_culturel/views.py:1182
+msgid "No events have been modified."
+msgstr "Aucun événement n'a été modifié."
+
+#: agenda_culturel/views.py:1135
+msgid "The place has been successfully updated."
+msgstr "Le lieu a été modifié avec succès."
+
+#: agenda_culturel/views.py:1142
+msgid "The place has been successfully created."
+msgstr "Le lieu a été créé avec succès."
+
+#: agenda_culturel/views.py:1201
+msgid "The selected place has been assigned to the event. "
+msgstr "Le lieu sélectionné a été assigné à l'événement. "
+
+#: agenda_culturel/views.py:1203
+msgid "A new alias has been added to the selected place. "
+msgstr "Un nouvel alias a été créé pour le lieu sélectionné. "
+
msgid "Recurrent import name"
msgstr "Nome de l'import récurrent"
diff --git a/src/agenda_culturel/migrations/0057_alter_place_aliases.py b/src/agenda_culturel/migrations/0057_alter_place_aliases.py
new file mode 100644
index 0000000..8bc7a40
--- /dev/null
+++ b/src/agenda_culturel/migrations/0057_alter_place_aliases.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.7 on 2024-04-27 07:44
+
+from django.db import migrations, models
+import django_better_admin_arrayfield.models.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('agenda_culturel', '0056_place_alter_event_location_event_exact_location'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='place',
+ name='aliases',
+ field=django_better_admin_arrayfield.models.fields.ArrayField(base_field=models.CharField(max_length=512), blank=True, help_text='Alternative names or addresses used to match a place with the free-form location of an event.', null=True, size=None, verbose_name='Alternative names'),
+ ),
+ ]
diff --git a/src/agenda_culturel/migrations/0058_place_city_alter_place_address.py b/src/agenda_culturel/migrations/0058_place_city_alter_place_address.py
new file mode 100644
index 0000000..6b3373b
--- /dev/null
+++ b/src/agenda_culturel/migrations/0058_place_city_alter_place_address.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.2.7 on 2024-04-27 15:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('agenda_culturel', '0057_alter_place_aliases'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='place',
+ name='city',
+ field=models.CharField(default='', help_text='City name', verbose_name='City'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='place',
+ name='address',
+ field=models.CharField(help_text='Address of this place (without city name)', verbose_name='Address'),
+ ),
+ ]
diff --git a/src/agenda_culturel/migrations/0059_auto_20240427_1829.py b/src/agenda_culturel/migrations/0059_auto_20240427_1829.py
new file mode 100644
index 0000000..771bbf1
--- /dev/null
+++ b/src/agenda_culturel/migrations/0059_auto_20240427_1829.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.7 on 2024-04-27 16:29
+
+from django.db import migrations
+from django.contrib.auth.management import create_permissions
+from django.contrib.auth.models import Group, Permission
+
+def update_groups_permissions(apps, schema_editor):
+
+ all_perms = Permission.objects.all()
+
+ # set permissions for moderators
+ moderator_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['place']]
+ Group.objects.get(name="Moderator").permissions.add(*moderator_perms)
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('agenda_culturel', '0058_place_city_alter_place_address'),
+ ]
+
+ operations = [
+ migrations.RunPython(update_groups_permissions),
+ ]
diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py
index 21debb5..b2790e6 100644
--- a/src/agenda_culturel/models.py
+++ b/src/agenda_culturel/models.py
@@ -160,8 +160,9 @@ class DuplicatedEvents(models.Model):
class Place(models.Model):
name = models.CharField(verbose_name=_('Name'), help_text=_('Name of the place'))
- address = models.CharField(verbose_name=_('Address'), help_text=_('Address of this place'))
- location = PlainLocationField(based_fields=['name', 'address'], zoom=12)
+ address = models.CharField(verbose_name=_('Address'), help_text=_('Address of this place (without city name)'))
+ city = models.CharField(verbose_name=_('City'), help_text=_('City name'))
+ location = PlainLocationField(based_fields=['name', 'address', 'city'], zoom=12)
aliases = ArrayField(models.CharField(max_length=512), verbose_name=_('Alternative names'), help_text=_("Alternative names or addresses used to match a place with the free-form location of an event."), blank=True, null=True)
@@ -175,6 +176,11 @@ class Place(models.Model):
def get_absolute_url(self):
return reverse("view_place", kwargs={"pk": self.pk})
+ def nb_events(self):
+ return Event.objects.filter(exact_location=self).count()
+
+ def match(self, event):
+ return event.location in self.aliases
class Event(models.Model):
@@ -396,9 +402,16 @@ class Event(models.Model):
if self.image and not self.local_image:
self.download_image()
- # try to detect category
if self.is_in_importation_process():
+ # try to detect category
CategorisationRule.apply_rules(self)
+ # try to detect location
+ if not self.exact_location:
+ for p in Place.objects.all():
+ if p.match(self):
+ logger.warning("Found a place for an imported event: " + p.name)
+ self.exact_location = p
+ break
def save(self, *args, **kwargs):
diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss
index 620f553..abbb21a 100644
--- a/src/agenda_culturel/static/style.scss
+++ b/src/agenda_culturel/static/style.scss
@@ -308,6 +308,10 @@ header .title {
}
}
+article form label {
+ display: inline-block;
+}
+
article#filters {
.buttons-filter {
float: right;
@@ -838,4 +842,12 @@ table .buttons {
border-top-right-radius: 0;
}
}
+}
+
+.missing-data {
+ color: red;
+}
+
+#filters #id_city {
+ columns: 4;
}
\ No newline at end of file
diff --git a/src/agenda_culturel/templates/agenda_culturel/event-location-inc.html b/src/agenda_culturel/templates/agenda_culturel/event-location-inc.html
new file mode 100644
index 0000000..7f51fab
--- /dev/null
+++ b/src/agenda_culturel/templates/agenda_culturel/event-location-inc.html
@@ -0,0 +1,14 @@
+
+{% if event.exact_location %}
+ {% if nolink %}
+ {{ event.exact_location.name }}, {{ event.exact_location.city }}
+ {% else %}
+ {{ event.exact_location.name }}, {{ event.exact_location.city }}
+ {% endif %}
+{% else %}
+ {% if perms.agenda_culturel.change_event and perms.agenda_culturel.change_place %}
+ {{ event.location }}
+ {% else %}
+ {{ event.location }}
+ {% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/src/agenda_culturel/templates/agenda_culturel/filter-inc.html b/src/agenda_culturel/templates/agenda_culturel/filter-inc.html
index 4bcfb65..dac6805 100644
--- a/src/agenda_culturel/templates/agenda_culturel/filter-inc.html
+++ b/src/agenda_culturel/templates/agenda_culturel/filter-inc.html
@@ -18,6 +18,9 @@
{% for s in filter.get_status_names %}
{{ s }}
{% endfor %}
+ {% for c in filter.get_cities %}
+ {{ c }}
+ {% endfor %}
{{ filter.get_recurrence_filtering }}
{% else %}
Filtrer
diff --git a/src/agenda_culturel/templates/agenda_culturel/page.html b/src/agenda_culturel/templates/agenda_culturel/page.html
index 4a4876f..aefbaed 100644
--- a/src/agenda_culturel/templates/agenda_culturel/page.html
+++ b/src/agenda_culturel/templates/agenda_culturel/page.html
@@ -52,6 +52,9 @@
{% if perms.agenda_culturel.change_duplicatedevents %}
{% show_badge_duplicated "bottom" %}
{% endif %}
+ {% if perms.agenda_culturel.change_place and perms.agenda_culturel.change_event %}
+ {% show_badge_unknown_places "bottom" %}
+ {% endif %}
{% if perms.agenda_culturel.view_contactmessage %}
{% show_badge_contactmessages "bottom" %}
{% endif %}
diff --git a/src/agenda_culturel/templates/agenda_culturel/place_confirm_delete.html b/src/agenda_culturel/templates/agenda_culturel/place_confirm_delete.html
index d066473..f2b6f51 100644
--- a/src/agenda_culturel/templates/agenda_culturel/place_confirm_delete.html
+++ b/src/agenda_culturel/templates/agenda_culturel/place_confirm_delete.html
@@ -10,8 +10,16 @@
Suppression du lieu {{ object.name }}
+ {% with nb_events=object.nb_events %}
+ {% if nb_events > 0 %}
+
Attention : {{ nb_events }} événement{{ nb_events|pluralize }}
+ {% if nb_events > 1 %}sont enregistrés{% else %}est enregistré{% endif %} dans ce lieu.
+ {% endif %}
+ {% endwith %}
+
Êtes-vous sûr·e de vouloir supprimer le lieu « {{ object.name }} ({{ object.pk }}) » situé à l'adresse {{ object.address }}, et de coordonnées {{ object.location }} ?