From 4e9ac573acc25b9d9d64648a7afea846c2995481 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 11:42:29 +0100 Subject: [PATCH 01/85] Consolidation en cas d'appel avec simple downloader --- src/agenda_culturel/import_tasks/generic_extractors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/import_tasks/generic_extractors.py b/src/agenda_culturel/import_tasks/generic_extractors.py index 10803c9..02f808f 100644 --- a/src/agenda_culturel/import_tasks/generic_extractors.py +++ b/src/agenda_culturel/import_tasks/generic_extractors.py @@ -264,7 +264,8 @@ class TwoStepsExtractorNoPause(TwoStepsExtractor): only_future=True, ignore_404=True ): - pause = self.downloader.pause + if hasattr(self.downloader, "pause"): + pause = self.downloader.pause self.downloader.pause = False result = super().extract(content, url, url_human, default_values, published, only_future, ignore_404) self.downloader.pause = pause From 7120da3e2891a72e5991b8a13aeb2f5e6ba9cd4f Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 11:44:40 +0100 Subject: [PATCH 02/85] =?UTF-8?q?On=20d=C3=A9fini=20une=20valeur=20par=20d?= =?UTF-8?q?=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/import_tasks/generic_extractors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/import_tasks/generic_extractors.py b/src/agenda_culturel/import_tasks/generic_extractors.py index 02f808f..14acc44 100644 --- a/src/agenda_culturel/import_tasks/generic_extractors.py +++ b/src/agenda_culturel/import_tasks/generic_extractors.py @@ -265,7 +265,9 @@ class TwoStepsExtractorNoPause(TwoStepsExtractor): ignore_404=True ): if hasattr(self.downloader, "pause"): - pause = self.downloader.pause + pause = self.downloader.pause + else: + pause = False self.downloader.pause = False result = super().extract(content, url, url_human, default_values, published, only_future, ignore_404) self.downloader.pause = pause From 1256adcb8af7c4577a5546b99e0ace38067eaa6b Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 12:16:02 +0100 Subject: [PATCH 03/85] On ajoute un parse de plus pour les dates --- src/agenda_culturel/import_tasks/extractor.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/agenda_culturel/import_tasks/extractor.py b/src/agenda_culturel/import_tasks/extractor.py index 3e75870..6822b47 100644 --- a/src/agenda_culturel/import_tasks/extractor.py +++ b/src/agenda_culturel/import_tasks/extractor.py @@ -49,7 +49,7 @@ class Extractor(ABC): return i + 1 return None - def parse_french_date(text): + def parse_french_date(text, default_year=None): # format NomJour Numero Mois Année m = re.search( "[a-zA-ZéÉûÛ:.]+[ ]*([0-9]+)[er]*[ ]*([a-zA-ZéÉûÛ:.]+)[ ]*([0-9]+)", text @@ -73,8 +73,15 @@ class Extractor(ABC): month = int(m.group(2)) year = m.group(3) else: - # TODO: consolider les cas non satisfaits - return None + # format Numero Mois Annee + m = re.search("([0-9]+)[er]*[ ]*([a-zA-ZéÉûÛ:.]+)", text) + if m: + day = m.group(1) + month = Extractor.guess_month(m.group(2)) + year = default_year + else: + # TODO: consolider les cas non satisfaits + return None if month is None: return None From bf773686f9661e55b9730a393e1c515189faaeef Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 12:43:20 +0100 Subject: [PATCH 04/85] L'image a une url absolue --- src/agenda_culturel/models.py | 7 +++++-- .../templates/agenda_culturel/page-event.html | 2 +- src/agenda_culturel/templatetags/event_extra.py | 6 +++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index d7b3909..9554190 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -923,10 +923,13 @@ class Event(models.Model): else: return recurrences[0] - def get_image_url(self): + def get_image_url(self, request=None): if self.local_image and hasattr(self.local_image, "url"): try: - return self.local_image.url + if request: + return request.build_absolute_uri(self.local_image.url) + else: + return self.local_image.url except: pass if self.image: diff --git a/src/agenda_culturel/templates/agenda_culturel/page-event.html b/src/agenda_culturel/templates/agenda_culturel/page-event.html index 142defe..42f0276 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-event.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-event.html @@ -6,7 +6,7 @@ {% block title %}{% block og_title %}{{ event.title }}{% endblock %}{% endblock %} -{% block og_image %}{% if event.has_image_url %}{{ event.get_image_url }}{% else %}{{ block.super }}{% endif %}{% endblock %} +{% block og_image %}{% if event.has_image_url %}{{ event|get_image_uri:request }}{% else %}{{ block.super }}{% endif %}{% endblock %} {% block og_description %}{% if event.description %}{{ event.description |truncatewords:20|linebreaks }}{% else %}{{ block.super }}{% endif %}{% endblock %} {% block entete_header %} diff --git a/src/agenda_culturel/templatetags/event_extra.py b/src/agenda_culturel/templatetags/event_extra.py index 32bdee7..4dd5be9 100644 --- a/src/agenda_culturel/templatetags/event_extra.py +++ b/src/agenda_culturel/templatetags/event_extra.py @@ -179,4 +179,8 @@ def tw_badge(event): if event.tags and len([t for t in event.tags if t.startswith("TW:")]) > 0: return mark_safe('TW') else: - return "" \ No newline at end of file + return "" + +@register.filter +def get_image_uri(event, request): + return event.get_image_url(request) \ No newline at end of file From ac3d6796cff7713b82d204aabc06bbdd1280884f Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 14:57:29 +0100 Subject: [PATCH 05/85] Ajout de l'import Rio Fix #187 --- experimentations/get_le_rio.py | 43 +++++++++ src/agenda_culturel/celery.py | 2 + .../import_tasks/custom_extractors/lerio.py | 91 +++++++++++++++++++ .../0122_alter_recurrentimport_processor.py | 18 ++++ src/agenda_culturel/models.py | 1 + 5 files changed, 155 insertions(+) create mode 100755 experimentations/get_le_rio.py create mode 100644 src/agenda_culturel/import_tasks/custom_extractors/lerio.py create mode 100644 src/agenda_culturel/migrations/0122_alter_recurrentimport_processor.py diff --git a/experimentations/get_le_rio.py b/experimentations/get_le_rio.py new file mode 100755 index 0000000..35e0364 --- /dev/null +++ b/experimentations/get_le_rio.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +# coding: utf-8 + +import os +import json +import sys + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +from src.agenda_culturel.import_tasks.downloader import * +from src.agenda_culturel.import_tasks.extractor import * +from src.agenda_culturel.import_tasks.importer import * +from src.agenda_culturel.import_tasks.custom_extractors import * + + + + + +if __name__ == "__main__": + + u2e = URL2Events(SimpleDownloader(), lerio.CExtractor()) + url = "https://www.cinemalerio.com/evenements/" + url_human = "https://www.cinemalerio.com/evenements/" + + try: + events = u2e.process(url, url_human, cache = "cache-le-rio.html", default_values = {"location": "Cinéma le Rio", "category": "Cinéma"}, published = True) + + exportfile = "events-le-roi.json" + print("Saving events to file {}".format(exportfile)) + with open(exportfile, "w") as f: + json.dump(events, f, indent=4, default=str) + except Exception as e: + print("Exception: " + str(e)) diff --git a/src/agenda_culturel/celery.py b/src/agenda_culturel/celery.py index b435900..f3ffaeb 100644 --- a/src/agenda_culturel/celery.py +++ b/src/agenda_culturel/celery.py @@ -147,6 +147,8 @@ def run_recurrent_import_internal(rimport, downloader, req_id): extractor = c3c.CExtractor() elif rimport.processor == RecurrentImport.PROCESSOR.ARACHNEE: extractor = arachnee.CExtractor() + elif rimport.processor == RecurrentImport.PROCESSOR.LERIO: + extractor = lerio.CExtractor() else: extractor = None diff --git a/src/agenda_culturel/import_tasks/custom_extractors/lerio.py b/src/agenda_culturel/import_tasks/custom_extractors/lerio.py new file mode 100644 index 0000000..117693c --- /dev/null +++ b/src/agenda_culturel/import_tasks/custom_extractors/lerio.py @@ -0,0 +1,91 @@ +from ..generic_extractors import * +from bs4 import BeautifulSoup +from datetime import datetime + +# A class dedicated to get events from Cinéma Le Rio (Clermont-Ferrand) +# URL: https://www.cinemalerio.com/evenements/ +class CExtractor(TwoStepsExtractorNoPause): + + def __init__(self): + super().__init__() + self.possible_dates = {} + self.theater = None + + def build_event_url_list(self, content, infuture_days=180): + + soup = BeautifulSoup(content, "html.parser") + + links = soup.select("td.seance_link a") + if links: + for l in links: + print(l["href"]) + self.add_event_url(l["href"]) + + def to_text_select_one(soup, filter): + e = soup.select_one(filter) + if e is None: + return None + else: + return e.text + + def add_event_from_content( + self, + event_content, + event_url, + url_human=None, + default_values=None, + published=False, + ): + + soup = BeautifulSoup(event_content, "html.parser") + + title = soup.select_one("h1").text + + alerte_date = CExtractor.to_text_select_one(soup, ".alerte_date") + if alerte_date is None: + return + dh = alerte_date.split("à") + # if date is not found, we skip + if len(dh) != 2: + return + + date = Extractor.parse_french_date(dh[0], default_year=datetime.now().year) + time = Extractor.parse_french_time(dh[1]) + + synopsis = CExtractor.to_text_select_one(soup, ".synopsis_bloc") + special_titre = CExtractor.to_text_select_one(soup, ".alerte_titre") + special = CExtractor.to_text_select_one(soup, ".alerte_text") + + # it's not a specific event: we skip it + special_lines = None if special is None else special.split('\n') + if special is None or len(special_lines) == 0 or \ + (len(special_lines) == 1 and special_lines[0].strip().startswith('En partenariat')): + return + + description = "\n\n".join([x for x in [synopsis, special_titre, special] if not x is None]) + + image = soup.select_one(".col1 img") + image_alt = None + if not image is None: + image_alt = image["alt"] + image = image["src"] + + self.add_event_with_props( + default_values, + event_url, + title, + None, + date, + None, + description, + [], + recurrences=None, + uuids=[event_url], + url_human=event_url, + start_time=time, + end_day=None, + end_time=None, + published=published, + image=image, + image_alt=image_alt + ) \ No newline at end of file diff --git a/src/agenda_culturel/migrations/0122_alter_recurrentimport_processor.py b/src/agenda_culturel/migrations/0122_alter_recurrentimport_processor.py new file mode 100644 index 0000000..0758649 --- /dev/null +++ b/src/agenda_culturel/migrations/0122_alter_recurrentimport_processor.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2024-11-29 13:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0121_contactmessage_related_event'), + ] + + operations = [ + migrations.AlterField( + model_name='recurrentimport', + name='processor', + field=models.CharField(choices=[('ical', 'ical'), ('icalnobusy', 'ical no busy'), ('icalnovc', 'ical no VC'), ('lacoope', 'lacoope.org'), ('lacomedie', 'la comédie'), ('lefotomat', 'le fotomat'), ('lapucealoreille', "la puce à l'oreille"), ('Plugin wordpress MEC', 'Plugin wordpress MEC'), ('Facebook events', "Événements d'une page FB"), ('cour3coquins', 'la cour des 3 coquins'), ('arachnee', 'Arachnée concert'), ('rio', 'Le Rio')], default='ical', max_length=20, verbose_name='Processor'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 9554190..03aeba3 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1767,6 +1767,7 @@ class RecurrentImport(models.Model): FBEVENTS = "Facebook events", _("Événements d'une page FB") C3C = "cour3coquins", _("la cour des 3 coquins") ARACHNEE = "arachnee", _("Arachnée concert") + LERIO = "rio", _('Le Rio') class DOWNLOADER(models.TextChoices): SIMPLE = "simple", _("simple") From c55ed5c4dc3668310a3097248c224a409c273f1a Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 15:44:20 +0100 Subject: [PATCH 06/85] Mise en forme recherche lieu --- .../templates/agenda_culturel/organisation_form.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/agenda_culturel/templates/agenda_culturel/organisation_form.html b/src/agenda_culturel/templates/agenda_culturel/organisation_form.html index 07116f5..c358c5b 100644 --- a/src/agenda_culturel/templates/agenda_culturel/organisation_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/organisation_form.html @@ -35,12 +35,8 @@ const places = document.querySelector('#id_principal_place'); const choices_places = new Choices(places, { - placeholderValue: 'Sélectionner le lieu principal ', - allowHTML: true, - delimiter: ',', - removeItemButton: true, shouldSort: false, - } + } ); From 7f1bbabebfd24d02c7e27b741fe6900f895b969b Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 29 Nov 2024 19:35:45 +0100 Subject: [PATCH 07/85] On enregistre l'auteur d'une modification Fix #228 --- src/agenda_culturel/celery.py | 8 +-- src/agenda_culturel/db_importer.py | 6 ++- src/agenda_culturel/forms.py | 6 ++- ...by_user_event_imported_by_user_and_more.py | 36 +++++++++++++ src/agenda_culturel/models.py | 52 ++++++++++++++++++- .../agenda_culturel/event-info-inc.html | 8 +-- .../agenda_culturel/event_form_moderate.html | 2 +- src/agenda_culturel/views.py | 11 ++-- 8 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 src/agenda_culturel/migrations/0123_event_created_by_user_event_imported_by_user_and_more.py diff --git a/src/agenda_culturel/celery.py b/src/agenda_culturel/celery.py index f3ffaeb..67672bb 100644 --- a/src/agenda_culturel/celery.py +++ b/src/agenda_culturel/celery.py @@ -291,7 +291,7 @@ def weekly_imports(self): run_recurrent_imports_from_list([imp.pk for imp in imports]) @app.task(base=ChromiumTask, bind=True) -def import_events_from_url(self, url, cat, tags, force=False): +def import_events_from_url(self, url, cat, tags, force=False, user_id=None): from .db_importer import DBImporterEvents from agenda_culturel.models import RecurrentImport, BatchImportation from agenda_culturel.models import Event, Category @@ -337,7 +337,7 @@ def import_events_from_url(self, url, cat, tags, force=False): json_events = json.dumps(events, default=str) # import events (from json) - success, error_message = importer.import_events(json_events) + success, error_message = importer.import_events(json_events, user_id) # finally, close task close_import_task(self.request.id, success, error_message, importer) @@ -354,14 +354,14 @@ def import_events_from_url(self, url, cat, tags, force=False): @app.task(base=ChromiumTask, bind=True) -def import_events_from_urls(self, urls_cat_tags): +def import_events_from_urls(self, urls_cat_tags, user_id=None): for ucat in urls_cat_tags: if ucat is not None: url = ucat[0] cat = ucat[1] tags = ucat[2] - import_events_from_url.delay(url, cat, tags) + import_events_from_url.delay(url, cat, tags, user_id=user_id) app.conf.beat_schedule = { diff --git a/src/agenda_culturel/db_importer.py b/src/agenda_culturel/db_importer.py index de38ce8..24e9c22 100644 --- a/src/agenda_culturel/db_importer.py +++ b/src/agenda_culturel/db_importer.py @@ -11,6 +11,7 @@ class DBImporterEvents: def __init__(self, celery_id): self.celery_id = celery_id self.error_message = "" + self.user_id = None self.init_result_properties() self.today = timezone.now().date().isoformat() @@ -34,9 +35,10 @@ class DBImporterEvents: def get_nb_removed_events(self): return self.nb_removed - def import_events(self, json_structure): + def import_events(self, json_structure, user_id=None): print(json_structure) self.init_result_properties() + self.user_id = user_id try: structure = json.loads(json_structure) @@ -95,7 +97,7 @@ class DBImporterEvents: def save_imported(self): self.db_event_objects, self.nb_updated, self.nb_removed = Event.import_events( - self.event_objects, remove_missing_from_source=self.url + self.event_objects, remove_missing_from_source=self.url, user_id=self.user_id ) def is_valid_event_structure(self, event): diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index f93b4db..314ad20 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -205,7 +205,11 @@ class EventForm(GroupFormMixin, ModelForm): "modified_date", "moderated_date", "import_sources", - "image" + "image", + "moderated_by_user", + "modified_by_user", + "created_by_user", + "imported_by_user" ] widgets = { "start_day": TextInput( diff --git a/src/agenda_culturel/migrations/0123_event_created_by_user_event_imported_by_user_and_more.py b/src/agenda_culturel/migrations/0123_event_created_by_user_event_imported_by_user_and_more.py new file mode 100644 index 0000000..a8359ce --- /dev/null +++ b/src/agenda_culturel/migrations/0123_event_created_by_user_event_imported_by_user_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.9 on 2024-11-29 18:18 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('agenda_culturel', '0122_alter_recurrentimport_processor'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='created_by_user', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='created_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the event creation'), + ), + migrations.AddField( + model_name='event', + name='imported_by_user', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='imported_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last importation'), + ), + migrations.AddField( + model_name='event', + name='moderated_by_user', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='moderated_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last moderation'), + ), + migrations.AddField( + model_name='event', + name='modified_by_user', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='modified_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last modification'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 03aeba3..40792ae 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -10,6 +10,7 @@ from colorfield.fields import ColorField from django_ckeditor_5.fields import CKEditor5Field from urllib.parse import urlparse from django.core.cache import cache +from django.contrib.auth.models import User import emoji import hashlib @@ -558,6 +559,39 @@ class Event(models.Model): modified_date = models.DateTimeField(blank=True, null=True) moderated_date = models.DateTimeField(blank=True, null=True) + created_by_user = models.ForeignKey( + User, + verbose_name=_("Author of the event creation"), + null=True, + default=None, + on_delete=models.SET_DEFAULT, + related_name="created_events" + ) + imported_by_user = models.ForeignKey( + User, + verbose_name=_("Author of the last importation"), + null=True, + default=None, + on_delete=models.SET_DEFAULT, + related_name="imported_events" + ) + modified_by_user = models.ForeignKey( + User, + verbose_name=_("Author of the last modification"), + null=True, + default=None, + on_delete=models.SET_DEFAULT, + related_name="modified_events" + ) + moderated_by_user = models.ForeignKey( + User, + verbose_name=_("Author of the last moderation"), + null=True, + default=None, + on_delete=models.SET_DEFAULT, + related_name="moderated_events" + ) + recurrence_dtstart = models.DateTimeField(editable=False, blank=True, null=True) recurrence_dtend = models.DateTimeField(editable=False, blank=True, null=True) @@ -692,6 +726,10 @@ class Event(models.Model): blank=True, ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.processing_user = None + def get_consolidated_end_day(self, intuitive=True): if intuitive: end_day = self.get_consolidated_end_day(False) @@ -896,6 +934,9 @@ class Event(models.Model): def set_no_modification_date_changed(self): self.no_modification_date_changed = True + def set_processing_user(self, user): + self.processing_user = user + def set_in_moderation_process(self): self.in_moderation_process = True @@ -906,12 +947,16 @@ class Event(models.Model): now = timezone.now() if not self.id: self.created_date = now + self.created_by_user = self.processing_user if self.is_in_importation_process(): self.imported_date = now + self.imported_by_user = self.processing_user if self.modified_date is None or not self.is_no_modification_date_changed(): self.modified_date = now + self.modified_by_user = self.processing_user if self.is_in_moderation_process(): self.moderated_date = now + self.moderated_by_user = self.processing_user def get_recurrence_at_date(self, year, month, day): dtstart = timezone.make_aware( @@ -1412,7 +1457,11 @@ class Event(models.Model): self.import_sources.append(source) # Limitation: the given events should not be considered similar one to another... - def import_events(events, remove_missing_from_source=None): + def import_events(events, remove_missing_from_source=None, user_id=None): + + user = None + if user_id: + user = User.objects.filter(pk=user_id).first() to_import = [] to_update = [] @@ -1439,6 +1488,7 @@ class Event(models.Model): # imported events should be updated event.set_in_importation_process() + event.set_processing_user(user) event.prepare_save() # check if the event has already be imported (using uuid) diff --git a/src/agenda_culturel/templates/agenda_culturel/event-info-inc.html b/src/agenda_culturel/templates/agenda_culturel/event-info-inc.html index fd968dc..4f1df6a 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event-info-inc.html +++ b/src/agenda_culturel/templates/agenda_culturel/event-info-inc.html @@ -1,10 +1,10 @@ diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index d6f266c..fd84eff 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -35,8 +35,8 @@ urlpatterns = [ ), path("event//edit", EventUpdateView.as_view(), name="edit_event"), path("event//moderate", EventModerateView.as_view(), name="moderate_event"), - path("event//moderate-next", EventModerateView.as_view(), name="moderate_event_next"), - path("event//moderate-next/error", error_next_event, name="error_next_event"), + path("event//moderate/after/", EventModerateView.as_view(), name="moderate_event_step"), + path("event//moderate-next", moderate_event_next, name="moderate_event_next"), path("moderate", EventModerateView.as_view(), name="moderate"), path("event//simple-clone/edit", EventUpdateView.as_view(), name="simple_clone_edit"), path("event//clone/edit", EventUpdateView.as_view(), name="clone_edit"), diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index a888639..5022f28 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -356,15 +356,18 @@ class EventModerateView( permission_required = "agenda_culturel.change_event" template_name = "agenda_culturel/event_form_moderate.html" form_class = EventModerateForm - success_message = _("The event has been successfully moderated.") + + def get_success_message(self, cleaned_data): + return mark_safe(_('The event {} has been moderated with success.').format(self.object.get_absolute_url(), self.object.title)) + def is_moderate_next(self): - return "moderate-next" in self.request.path.split('/') + return "after" in self.request.path.split('/') def is_starting_moderation(self): return not "pk" in self.kwargs - def get_next_event(self, start_day, start_time): + def get_next_event(start_day, start_time): # select non moderated events qs = Event.objects.filter(moderated_date__isnull=True) @@ -391,19 +394,15 @@ class EventModerateView( def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.is_moderate_next(): - context['pred'] = self.kwargs["pk"] + context['pred'] = self.kwargs["pred"] return context def get_object(self, queryset=None): if self.is_starting_moderation(): now = datetime.now() - return self.get_next_event(now.date(), now.time()) + return EventModerateView.get_next_event(now.date(), now.time()) else: - result = super().get_object(queryset) - if self.is_moderate_next(): - return self.get_next_event(result.start_day, result.start_time) - else: - return result + return super().get_object(queryset) def post(self, request, *args, **kwargs): try: @@ -421,10 +420,6 @@ class EventModerateView( def get_success_url(self): if 'save_and_next' in self.request.POST: return reverse_lazy("moderate_event_next", args=[self.object.pk]) - elif 'save_and_create_local' in self.request.POST: - return reverse_lazy("clone_edit", args=[self.object.pk]) - elif 'save_and_edit' in self.request.POST: - return reverse_lazy("edit_event", args=[self.object.pk]) elif 'save_and_edit_local' in self.request.POST: return reverse_lazy("edit_event", args=[self.object.get_local_version().pk]) else: @@ -443,6 +438,23 @@ def error_next_event(request, pk): {"pk": pk, "object": obj}, ) +@login_required(login_url="/accounts/login/") +@permission_required("agenda_culturel.change_event") +def moderate_event_next(request, pk): + # current event + obj = Event.objects.filter(pk=pk).first() + start_day = obj.start_day + start_time = obj.start_time + + next_obj = EventModerateView.get_next_event(start_day, start_time) + if next_obj is None: + return render( + request, + "agenda_culturel/event_next_error_message.html", + {"pk": pk, "object": obj}, + ) + else: + return HttpResponseRedirect(reverse_lazy("moderate_event_step", args=[next_obj.pk, obj.pk])) class EventDeleteView( From c85729434571734591708e44889bb635f68ce66f Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Thu, 5 Dec 2024 21:32:35 +0100 Subject: [PATCH 24/85] Fix bug fusion manuelle --- src/agenda_culturel/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index 443a11d..cd7ced7 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -641,7 +641,7 @@ class MergeDuplicates(Form): result = [] for s in selected: for e in self.duplicates.get_duplicated(): - if e.pk == selected: + if e.pk == s: result.append(e) break return result From b3cba9293c229bfd7f1211b3ebd224d1a0bec40c Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 11:28:20 +0100 Subject: [PATCH 25/85] =?UTF-8?q?On=20ajoute=20l'url=20probl=C3=A9matique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/import_tasks/custom_extractors/fbevents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py b/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py index ca74ec6..bf02a96 100644 --- a/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py +++ b/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py @@ -34,6 +34,7 @@ class CExtractor(TwoStepsExtractor): filename = directory + now.strftime("%Y%m%d_%H%M%S") + ".html" logger.warning("cannot find any event link in events page. Save content page in " + filename) with open(filename, "w") as text_file: + text_file.write("\n\n") text_file.write(content) From 27ceac1e46fc12a769cfb7bb4d3e64f492b3ce0b Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 14:18:32 +0100 Subject: [PATCH 26/85] Ajout d'un espace manquant --- src/agenda_culturel/templatetags/contactmessages_extra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/templatetags/contactmessages_extra.py b/src/agenda_culturel/templatetags/contactmessages_extra.py index 09ad428..b801c44 100644 --- a/src/agenda_culturel/templatetags/contactmessages_extra.py +++ b/src/agenda_culturel/templatetags/contactmessages_extra.py @@ -20,7 +20,7 @@ def show_badge_contactmessages(placement="top"): + reverse_lazy("contactmessages") + '?closed=False" class="badge" data-placement="' + placement - + '"data-tooltip="' + + '" data-tooltip="' + str(nb_open) + " message" + pluralize(nb_open) From 91907be9840d3c28444f02f9fce10c5fd6881440 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 18:10:11 +0100 Subject: [PATCH 27/85] 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): From a31bcc27646ae159f46cfcb07bd31e5e038dc4f7 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 19:48:32 +0100 Subject: [PATCH 28/85] On modifie l'outil de localisation pour ajouter le lock Fix #124 --- src/agenda_culturel/forms.py | 20 +- .../static/location_field/js/form.js | 673 ++++++++++++++++++ src/agenda_culturel/views.py | 8 +- 3 files changed, 688 insertions(+), 13 deletions(-) create mode 100644 src/agenda_culturel/static/location_field/js/form.js diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index e121ef9..697e192 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -742,15 +742,23 @@ 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): result = ('
' + super().as_p() - + '
' - + '

Cliquez pour ajuster la position GPS

') + + '''
+
+

Cliquez pour ajuster la position GPS

+ Verrouiller la position + +
''') return mark_safe(result) diff --git a/src/agenda_culturel/static/location_field/js/form.js b/src/agenda_culturel/static/location_field/js/form.js new file mode 100644 index 0000000..85f9cc3 --- /dev/null +++ b/src/agenda_culturel/static/location_field/js/form.js @@ -0,0 +1,673 @@ +var SequentialLoader = function() { + var SL = { + loadJS: function(src, onload) { + //console.log(src); + // add to pending list + this._load_pending.push({'src': src, 'onload': onload}); + // check if not already loading + if ( ! this._loading) { + this._loading = true; + // load first + this.loadNextJS(); + } + }, + + loadNextJS: function() { + // get next + var next = this._load_pending.shift(); + if (next == undefined) { + // nothing to load + this._loading = false; + return; + } + // check not loaded + if (this._load_cache[next.src] != undefined) { + next.onload(); + this.loadNextJS(); + return; // already loaded + } + else { + this._load_cache[next.src] = 1; + } + // load + var el = document.createElement('script'); + el.type = 'application/javascript'; + el.src = next.src; + // onload callback + var self = this; + el.onload = function(){ + //console.log('Loaded: ' + next.src); + // trigger onload + next.onload(); + // try to load next + self.loadNextJS(); + }; + document.body.appendChild(el); + }, + + _loading: false, + _load_pending: [], + _load_cache: {} + }; + + return { + loadJS: SL.loadJS.bind(SL) + } +}; + + +!function($){ + var LocationFieldCache = { + load: [], + onload: {}, + + isLoading: false + }; + + var LocationFieldResourceLoader; + + $.locationField = function(options) { + var LocationField = { + options: $.extend({ + provider: 'google', + providerOptions: { + google: { + api: '//maps.google.com/maps/api/js', + mapType: 'ROADMAP' + } + }, + searchProvider: 'google', + id: 'map', + latLng: '0,0', + mapOptions: { + zoom: 9 + }, + basedFields: $(), + inputField: $(), + suffix: '', + path: '', + fixMarker: true + }, options), + + providers: /google|openstreetmap|mapbox/, + searchProviders: /google|yandex|nominatim|addok/, + + render: function() { + this.$id = $('#' + this.options.id); + + if ( ! this.providers.test(this.options.provider)) { + this.error('render failed, invalid map provider: ' + this.options.provider); + return; + } + + if ( ! this.searchProviders.test(this.options.searchProvider)) { + this.error('render failed, invalid search provider: ' + this.options.searchProvider); + return; + } + + var self = this; + + this.loadAll(function(){ + var mapOptions = self._getMapOptions(), + map = self._getMap(mapOptions); + + var marker = self._getMarker(map, mapOptions.center); + + // fix issue w/ marker not appearing + if (self.options.provider == 'google' && self.options.fixMarker) + self.__fixMarker(); + + // watch based fields + self._watchBasedFields(map, marker); + }); + }, + + fill: function(latLng) { + this.options.inputField.val(latLng.lat + ',' + latLng.lng); + }, + + search: function(map, marker, address) { + if (this.options.searchProvider === 'google') { + var provider = new GeoSearch.GoogleProvider({ apiKey: this.options.providerOptions.google.apiKey }); + provider.search({query: address}).then(data => { + if (data.length > 0) { + var result = data[0], + latLng = new L.LatLng(result.y, result.x); + + marker.setLatLng(latLng); + map.panTo(latLng); + } + }); + } + + else if (this.options.searchProvider === 'yandex') { + // https://yandex.com/dev/maps/geocoder/doc/desc/concepts/input_params.html + var url = 'https://geocode-maps.yandex.ru/1.x/?format=json&geocode=' + address; + + if (typeof this.options.providerOptions.yandex.apiKey !== 'undefined') { + url += '&apikey=' + this.options.providerOptions.yandex.apiKey; + } + + var request = new XMLHttpRequest(); + request.open('GET', url, true); + + request.onload = function () { + if (request.status >= 200 && request.status < 400) { + var data = JSON.parse(request.responseText); + var pos = data.response.GeoObjectCollection.featureMember[0].GeoObject.Point.pos.split(' '); + var latLng = new L.LatLng(pos[1], pos[0]); + marker.setLatLng(latLng); + map.panTo(latLng); + } else { + console.error('Yandex geocoder error response'); + } + }; + + request.onerror = function () { + console.error('Check connection to Yandex geocoder'); + }; + + request.send(); + } + + else if (this.options.searchProvider === 'addok') { + var url = 'https://api-adresse.data.gouv.fr/search/?limit=1&q=' + address; + + var request = new XMLHttpRequest(); + request.open('GET', url, true); + + request.onload = function () { + if (request.status >= 200 && request.status < 400) { + var data = JSON.parse(request.responseText); + var pos = data.features[0].geometry.coordinates; + var latLng = new L.LatLng(pos[1], pos[0]); + marker.setLatLng(latLng); + map.panTo(latLng); + } else { + console.error('Addok geocoder error response'); + } + }; + + request.onerror = function () { + console.error('Check connection to Addok geocoder'); + }; + + request.send(); + } + + else if (this.options.searchProvider === 'nominatim') { + var url = '//nominatim.openstreetmap.org/search?format=json&q=' + address; + + var request = new XMLHttpRequest(); + request.open('GET', url, true); + + request.onload = function () { + if (request.status >= 200 && request.status < 400) { + var data = JSON.parse(request.responseText); + if (data.length > 0) { + var pos = data[0]; + var latLng = new L.LatLng(pos.lat, pos.lon); + marker.setLatLng(latLng); + map.panTo(latLng); + } else { + console.error(address + ': not found via Nominatim'); + } + } else { + console.error('Nominatim geocoder error response'); + } + }; + + request.onerror = function () { + console.error('Check connection to Nominatim geocoder'); + }; + + request.send(); + } + }, + + loadAll: function(onload) { + this.$id.html('Loading...'); + + // resource loader + if (LocationFieldResourceLoader == undefined) + LocationFieldResourceLoader = SequentialLoader(); + + this.load.loader = LocationFieldResourceLoader; + this.load.path = this.options.path; + + var self = this; + + this.load.common(function(){ + var mapProvider = self.options.provider, + onLoadMapProvider = function() { + var searchProvider = self.options.searchProvider + 'SearchProvider', + onLoadSearchProvider = function() { + self.$id.html(''); + onload(); + }; + + if (self.load[searchProvider] != undefined) { + self.load[searchProvider](self.options.providerOptions[self.options.searchProvider] || {}, onLoadSearchProvider); + } + else { + onLoadSearchProvider(); + } + }; + + if (self.load[mapProvider] != undefined) { + self.load[mapProvider](self.options.providerOptions[mapProvider] || {}, onLoadMapProvider); + } + else { + onLoadMapProvider(); + } + }); + }, + + load: { + google: function(options, onload) { + var js = [ + this.path + '/@googlemaps/js-api-loader/index.min.js', + this.path + '/Leaflet.GoogleMutant.js', + ]; + + this._loadJSList(js, function(){ + const loader = new google.maps.plugins.loader.Loader({ + apiKey: options.apiKey, + version: "weekly", + }); + loader.load().then(() => onload()); + }); + }, + + googleSearchProvider: function(options, onload) { + onload(); + //var url = options.api; + + //if (typeof options.apiKey !== 'undefined') { + // url += url.indexOf('?') === -1 ? '?' : '&'; + // url += 'key=' + options.apiKey; + //} + + //var js = [ + // url, + // this.path + '/l.geosearch.provider.google.js' + // ]; + + //this._loadJSList(js, function(){ + // // https://github.com/smeijer/L.GeoSearch/issues/57#issuecomment-148393974 + // L.GeoSearch.Provider.Google.Geocoder = new google.maps.Geocoder(); + + // onload(); + //}); + }, + + yandexSearchProvider: function (options, onload) { + onload(); + }, + + mapbox: function(options, onload) { + onload(); + }, + + openstreetmap: function(options, onload) { + onload(); + }, + + common: function(onload) { + var self = this, + js = [ + // map providers + this.path + '/leaflet/leaflet.js', + // search providers + this.path + '/leaflet-geosearch/geosearch.umd.js', + ], + css = [ + // map providers + this.path + '/leaflet/leaflet.css' + ]; + + // Leaflet docs note: + // Include Leaflet JavaScript file *after* Leaflet’s CSS + // https://leafletjs.com/examples/quick-start/ + this._loadCSSList(css, function(){ + self._loadJSList(js, onload); + }); + }, + + _loadJS: function(src, onload) { + this.loader.loadJS(src, onload); + }, + + _loadJSList: function(srclist, onload) { + this.__loadList(this._loadJS, srclist, onload); + }, + + _loadCSS: function(src, onload) { + if (LocationFieldCache.onload[src] != undefined) { + onload(); + } + else { + LocationFieldCache.onload[src] = 1; + onloadCSS(loadCSS(src), onload); + } + }, + + _loadCSSList: function(srclist, onload) { + this.__loadList(this._loadCSS, srclist, onload); + }, + + __loadList: function(fn, srclist, onload) { + if (srclist.length > 1) { + for (var i = 0; i < srclist.length-1; ++i) { + fn.call(this, srclist[i], function(){}); + } + } + + fn.call(this, srclist[srclist.length-1], onload); + } + }, + + error: function(message) { + console.log(message); + this.$id.html(message); + }, + + _getMap: function(mapOptions) { + var map = new L.Map(this.options.id, mapOptions), layer; + + if (this.options.provider == 'google') { + layer = new L.gridLayer.googleMutant({ + type: this.options.providerOptions.google.mapType.toLowerCase(), + }); + } + else if (this.options.provider == 'openstreetmap') { + layer = new L.tileLayer( + '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 18 + }); + } + else if (this.options.provider == 'mapbox') { + layer = new L.tileLayer( + 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { + maxZoom: 18, + accessToken: this.options.providerOptions.mapbox.access_token, + id: 'mapbox/streets-v11' + }); + } + + map.addLayer(layer); + + return map; + }, + + _getMapOptions: function() { + return $.extend(this.options.mapOptions, { + center: this._getLatLng() + }); + }, + + _getLatLng: function() { + var l = this.options.latLng.split(',').map(parseFloat); + return new L.LatLng(l[0], l[1]); + }, + + _getMarker: function(map, center) { + var self = this, + markerOptions = { + draggable: true + }; + + var marker = L.marker(center, markerOptions).addTo(map); + + marker.on('dragstart', function(){ + if (self.options.inputField.is('[readonly]')) + marker.dragging.disable(); + else + marker.dragging.enable(); + }); + + // fill input on dragend + marker.on('dragend move', function(){ + if (!self.options.inputField.is('[readonly]')) + self.fill(this.getLatLng()); + }); + + // place marker on map click + map.on('click', function(e){ + if (!self.options.inputField.is('[readonly]')) { + marker.setLatLng(e.latlng); + marker.dragging.enable(); + } + }); + + return marker; + }, + + _watchBasedFields: function(map, marker) { + var self = this, + basedFields = this.options.basedFields, + onchangeTimer, + onchange = function() { + if (!self.options.inputField.is('[readonly]')) { + var values = basedFields.map(function() { + var value = $(this).val(); + return value === '' ? null : value; + }); + var address = values.toArray().join(', '); + clearTimeout(onchangeTimer); + onchangeTimer = setTimeout(function(){ + self.search(map, marker, address); + }, 300); + } + }; + + basedFields.each(function(){ + var el = $(this); + + if (el.is('select')) + el.change(onchange); + else + el.keyup(onchange); + }); + + if (this.options.inputField.val() === '') { + var values = basedFields.map(function() { + var value = $(this).val(); + return value === '' ? null : value; + }); + var address = values.toArray().join(', '); + if (address !== '') + onchange(); + } + }, + + __fixMarker: function() { + $('.leaflet-map-pane').css('z-index', '2 !important'); + $('.leaflet-google-layer').css('z-index', '1 !important'); + } + } + + return { + render: LocationField.render.bind(LocationField) + } + } + + function dataLocationFieldObserver(callback) { + function _findAndEnableDataLocationFields() { + var dataLocationFields = $('input[data-location-field-options]'); + + dataLocationFields + .filter(':not([data-location-field-observed])') + .attr('data-location-field-observed', true) + .each(callback); + } + + var observer = new MutationObserver(function(mutations){ + _findAndEnableDataLocationFields(); + }); + + var container = document.documentElement || document.body; + + $(container).ready(function(){ + _findAndEnableDataLocationFields(); + }); + + observer.observe(container, {attributes: true}); + } + + dataLocationFieldObserver(function(){ + var el = $(this); + + var name = el.attr('name'), + options = el.data('location-field-options'), + basedFields = options.field_options.based_fields, + pluginOptions = { + id: 'map_' + name, + inputField: el, + latLng: el.val() || '0,0', + suffix: options['search.suffix'], + path: options['resources.root_path'], + provider: options['map.provider'], + searchProvider: options['search.provider'], + providerOptions: { + google: { + api: options['provider.google.api'], + apiKey: options['provider.google.api_key'], + mapType: options['provider.google.map_type'] + }, + mapbox: { + access_token: options['provider.mapbox.access_token'] + }, + yandex: { + apiKey: options['provider.yandex.api_key'] + }, + }, + mapOptions: { + zoom: options['map.zoom'] + } + }; + + // prefix + var prefixNumber; + + try { + prefixNumber = name.match(/-(\d+)-/)[1]; + } catch (e) {} + + if (options.field_options.prefix) { + var prefix = options.field_options.prefix; + + if (prefixNumber != null) { + prefix = prefix.replace(/__prefix__/, prefixNumber); + } + + basedFields = basedFields.map(function(n){ + return prefix + n + }); + } + + // based fields + pluginOptions.basedFields = $(basedFields.map(function(n){ + return '#id_' + n + }).join(',')); + + // render + $.locationField(pluginOptions).render(); + }); + +}(jQuery || django.jQuery); + +/*! +loadCSS: load a CSS file asynchronously. +[c]2015 @scottjehl, Filament Group, Inc. +Licensed MIT +*/ +(function(w){ + "use strict"; + /* exported loadCSS */ + var loadCSS = function( href, before, media ){ + // Arguments explained: + // `href` [REQUIRED] is the URL for your CSS file. + // `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet before + // By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document. + // `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all' + var doc = w.document; + var ss = doc.createElement( "link" ); + var ref; + if( before ){ + ref = before; + } + else { + var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes; + ref = refs[ refs.length - 1]; + } + + var sheets = doc.styleSheets; + ss.rel = "stylesheet"; + ss.href = href; + // temporarily set media to something inapplicable to ensure it'll fetch without blocking render + ss.media = "only x"; + + // Inject link + // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs + // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/ + ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) ); + // A method (exposed on return object for external use) that mimics onload by polling until document.styleSheets until it includes the new sheet. + var onloadcssdefined = function( cb ){ + var resolvedHref = ss.href; + var i = sheets.length; + while( i-- ){ + if( sheets[ i ].href === resolvedHref ){ + return cb(); + } + } + setTimeout(function() { + onloadcssdefined( cb ); + }); + }; + + // once loaded, set link's media back to `all` so that the stylesheet applies once it loads + ss.onloadcssdefined = onloadcssdefined; + onloadcssdefined(function() { + ss.media = media || "all"; + }); + return ss; + }; + // commonjs + if( typeof module !== "undefined" ){ + module.exports = loadCSS; + } + else { + w.loadCSS = loadCSS; + } +}( typeof global !== "undefined" ? global : this )); + + +/*! +onloadCSS: adds onload support for asynchronous stylesheets loaded with loadCSS. +[c]2014 @zachleat, Filament Group, Inc. +Licensed MIT +*/ + +/* global navigator */ +/* exported onloadCSS */ +function onloadCSS( ss, callback ) { + ss.onload = function() { + ss.onload = null; + if( callback ) { + callback.call( ss ); + } + }; + + // This code is for browsers that don’t support onload, any browser that + // supports onload should use that instead. + // No support for onload: + // * Android 4.3 (Samsung Galaxy S4, Browserstack) + // * Android 4.2 Browser (Samsung Galaxy SIII Mini GT-I8200L) + // * Android 2.3 (Pantech Burst P9070) + + // Weak inference targets Android < 4.4 + if( "isApplicationInstalled" in navigator && "onloadcssdefined" in ss ) { + ss.onloadcssdefined( callback ); + } +} diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index 46b7f77..eb7aa39 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -1871,13 +1871,6 @@ class PlaceFromEventCreateView(PlaceCreateView): 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"]) @@ -1888,6 +1881,7 @@ class PlaceFromEventCreateView(PlaceCreateView): initial["name"] = name initial["address"] = address initial["city"] = city + initial["location"] = "" return initial From bd1330cd2fdbd5375a401d6c1463126f972b5cc4 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 23:22:38 +0100 Subject: [PATCH 29/85] =?UTF-8?q?Correction=20du=20nom=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index 697e192..b37c35d 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -75,7 +75,7 @@ class GroupFormMixin: return [f for f in self.visible_fields() if not hasattr(f.field, "toggle_group") and (not hasattr(f.field, "group_id") or f.field.group_id == None)] def fields_by_group(self): - return [(g, self.get_fields_in_group(g)) for g in self.groups] + [(None, self.get_no_group_fields())] + return [(g, self.get_fields_in_group(g)) for g in self.groups] + [(GroupFormMixin.FieldGroup("other", _("Other")), self.get_no_group_fields())] def clean(self): result = super().clean() From f52caf98551af8337eb8dbabfd7e897731b8ead3 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 23:24:08 +0100 Subject: [PATCH 30/85] =?UTF-8?q?Ajout=20d'une=20entr=C3=A9e=20code=20post?= =?UTF-8?q?al?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0124_place_postcode.py | 18 ++++++++++++++++++ src/agenda_culturel/models.py | 3 ++- src/agenda_culturel/views.py | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/agenda_culturel/migrations/0124_place_postcode.py diff --git a/src/agenda_culturel/migrations/0124_place_postcode.py b/src/agenda_culturel/migrations/0124_place_postcode.py new file mode 100644 index 0000000..9581868 --- /dev/null +++ b/src/agenda_culturel/migrations/0124_place_postcode.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2024-12-06 21:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0123_event_created_by_user_event_imported_by_user_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='place', + name='postcode', + field=models.CharField(blank=True, help_text='The post code is not displayed, but makes it easier to find an address when you enter it.', null=True, verbose_name='Postcode'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 5a5a969..510c06b 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -435,8 +435,9 @@ class Place(models.Model): blank=True, null=True, ) + postcode = models.CharField(verbose_name=_("Postcode"), help_text=_("The post code is not displayed, but makes it easier to find an address when you enter it."), blank=True, null=True) 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", "postcode", "city"], zoom=12, default=Point(3.08333, 45.783329)) description = CKEditor5Field( verbose_name=_("Description"), diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index eb7aa39..27eed27 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -1880,6 +1880,7 @@ class PlaceFromEventCreateView(PlaceCreateView): 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"] = "" From c34abe91589fd7d3220d9ad95b3cd60cbc1b6851 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 23:24:31 +0100 Subject: [PATCH 31/85] Restructuration des champs du formulaire de lieu --- src/agenda_culturel/forms.py | 21 +++++++++++- src/agenda_culturel/static/style.scss | 34 +++++++++++++++++++ .../templates/agenda_culturel/place_form.html | 18 +++++++++- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index b37c35d..30e3e85 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -726,7 +726,7 @@ class EventAddPlaceForm(Form): return self.instance -class PlaceForm(ModelForm): +class PlaceForm(GroupFormMixin, ModelForm): required_css_class = 'required' apply_to_all = BooleanField( @@ -742,6 +742,25 @@ class PlaceForm(ModelForm): fields = "__all__" widgets = {"location": TextInput()} + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.add_group('header', _('Header')) + self.fields['name'].group_id = 'header' + + + self.add_group('address', _('Address')) + self.fields['address'].group_id = 'address' + self.fields['postcode'].group_id = 'address' + self.fields['city'].group_id = 'address' + self.fields['location'].group_id = 'address' + + self.add_group('meta', _('Meta')) + self.fields['aliases'].group_id = 'meta' + + self.add_group('information', _('Information')) + self.fields['description'].group_id = 'information' + def as_grid(self): result = ('
' + super().as_p() diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index 160ab9d..260b4ee 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -1518,3 +1518,37 @@ label.required::after { .maskable_group .body_group.closed { display: none; } + +.form-place { + display: grid; + grid-template-columns: repeat(1, 1fr); + row-gap: .5em; + margin-bottom: 0.5em; + .map-widget { + grid-row: 3; + } + #group_address .body_group { + display: grid; + grid-template-columns: repear(2, 1fr); + + column-gap: .5em; + #div_id_address, #div_id_location { + grid-column: 1 / 3; + } + + } +} +@media only screen and (min-width: 992px) { + .form-place { + grid-template-columns: repeat(2, 1fr); + + .map-widget { + grid-column: 2 / 3; + grid-row: 1 / 3; + } + + #group_other { + grid-column: 1 / 3; + } + } +} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/place_form.html b/src/agenda_culturel/templates/agenda_culturel/place_form.html index c06d41b..b034fd2 100644 --- a/src/agenda_culturel/templates/agenda_culturel/place_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/place_form.html @@ -25,7 +25,23 @@

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 }} +
+ {{ form }} +
+
+

Cliquez pour ajuster la position GPS

+ Verrouiller la position + +
+
Annuler From b0b828392a20ce3690c45f04e1b1032d7d10edf3 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Fri, 6 Dec 2024 23:24:57 +0100 Subject: [PATCH 32/85] Traduction --- .../locale/fr/LC_MESSAGES/django.po | 484 +++++++++--------- 1 file changed, 247 insertions(+), 237 deletions(-) diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po index 59fd0b2..0394d28 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-12-05 20:50+0100\n" +"POT-Creation-Date: 2024-12-06 22:38+0100\n" "PO-Revision-Date: 2023-10-29 14:16+0000\n" "Last-Translator: Jean-Marie Favreau \n" "Language-Team: Jean-Marie Favreau \n" @@ -110,7 +110,7 @@ msgstr "Non" msgid "Imported from" msgstr "Importé depuis" -#: agenda_culturel/filters.py:362 agenda_culturel/models.py:1785 +#: agenda_culturel/filters.py:362 agenda_culturel/models.py:1786 msgid "Closed" msgstr "Fermé" @@ -118,7 +118,7 @@ msgstr "Fermé" msgid "Open" msgstr "Ouvert" -#: agenda_culturel/filters.py:367 agenda_culturel/models.py:1779 +#: agenda_culturel/filters.py:367 agenda_culturel/models.py:1780 msgid "Spam" msgstr "Spam" @@ -130,79 +130,79 @@ msgstr "Non spam" msgid "Search" msgstr "Rechercher" -#: agenda_culturel/forms.py:107 +#: agenda_culturel/forms.py:108 msgid "Name of new tag" msgstr "Nom de la nouvelle étiquette" -#: agenda_culturel/forms.py:112 +#: agenda_culturel/forms.py:113 msgid "" "Force renaming despite the existence of events already using the chosen tag." msgstr "" "Forcer le renommage malgré l'existence d'événements utilisant déjà " "l'étiquette choisie." -#: agenda_culturel/forms.py:133 agenda_culturel/models.py:176 -#: agenda_culturel/models.py:608 agenda_culturel/models.py:1904 -#: agenda_culturel/models.py:2009 +#: agenda_culturel/forms.py:134 agenda_culturel/models.py:176 +#: agenda_culturel/models.py:609 agenda_culturel/models.py:1905 +#: agenda_culturel/models.py:2010 msgid "Category" msgstr "Catégorie" -#: agenda_culturel/forms.py:139 agenda_culturel/forms.py:164 -#: agenda_culturel/forms.py:194 agenda_culturel/forms.py:349 -#: agenda_culturel/models.py:217 agenda_culturel/models.py:716 +#: agenda_culturel/forms.py:140 agenda_culturel/forms.py:165 +#: agenda_culturel/forms.py:195 agenda_culturel/forms.py:350 +#: agenda_culturel/models.py:217 agenda_culturel/models.py:717 msgid "Tags" msgstr "Étiquettes" -#: agenda_culturel/forms.py:250 +#: agenda_culturel/forms.py:251 msgid "Main fields" msgstr "Champs principaux" -#: agenda_culturel/forms.py:253 +#: agenda_culturel/forms.py:254 msgid "Start of event" msgstr "Début de l'événement" -#: agenda_culturel/forms.py:257 +#: agenda_culturel/forms.py:258 msgid "End of event" msgstr "Fin de l'événement" -#: agenda_culturel/forms.py:262 +#: agenda_culturel/forms.py:263 msgid "This is a recurring event" msgstr "Cet événement est récurrent" -#: agenda_culturel/forms.py:271 +#: agenda_culturel/forms.py:272 msgid "Details" msgstr "Détails" -#: agenda_culturel/forms.py:276 agenda_culturel/models.py:638 -#: agenda_culturel/models.py:1885 +#: agenda_culturel/forms.py:277 agenda_culturel/models.py:639 +#: agenda_culturel/models.py:1886 msgid "Location" msgstr "Localisation" -#: agenda_culturel/forms.py:280 agenda_culturel/models.py:671 +#: agenda_culturel/forms.py:281 agenda_culturel/models.py:672 msgid "Illustration" msgstr "Illustration" -#: agenda_culturel/forms.py:286 agenda_culturel/forms.py:291 +#: agenda_culturel/forms.py:287 agenda_culturel/forms.py:292 msgid "Meta information" msgstr "Méta-informations" -#: agenda_culturel/forms.py:306 +#: agenda_culturel/forms.py:307 msgid "The end date must be after the start date." msgstr "La date de fin doit être après la date de début." -#: agenda_culturel/forms.py:322 +#: agenda_culturel/forms.py:323 msgid "The end time cannot be earlier than the start time." msgstr "L'heure de fin ne peut pas être avant l'heure de début." -#: agenda_culturel/forms.py:350 +#: agenda_culturel/forms.py:351 msgid "Select tags from existing ones." msgstr "Sélectionner des étiquettes depuis celles existantes." -#: agenda_culturel/forms.py:355 +#: agenda_culturel/forms.py:356 msgid "New tags" msgstr "Nouvelles étiquettes" -#: agenda_culturel/forms.py:356 +#: agenda_culturel/forms.py:357 msgid "" "Create new labels (sparingly). Note: by starting your tag with the " "characters “TW:”, youll create a “trigger warning” tag, and the associated " @@ -213,74 +213,74 @@ msgstr "" "étiquette “trigger warning”, et les événements associés seront annoncés " "comme tels." -#: agenda_culturel/forms.py:402 +#: agenda_culturel/forms.py:403 msgid "JSON in the format expected for the import." msgstr "JSON dans le format attendu pour l'import" -#: agenda_culturel/forms.py:424 +#: agenda_culturel/forms.py:425 msgid " (locally modified version)" msgstr " (version modifiée localement)" -#: agenda_culturel/forms.py:428 +#: agenda_culturel/forms.py:429 msgid " (synchronized on import version)" msgstr " (version synchronisée sur l'import)" -#: agenda_culturel/forms.py:432 +#: agenda_culturel/forms.py:433 msgid "Select {} as representative version." msgstr "Sélectionner {} comme version représentative" -#: agenda_culturel/forms.py:441 +#: agenda_culturel/forms.py:442 msgid "Update {} using some fields from other versions (interactive mode)." msgstr "" "Mettre à jour {} en utilisant quelques champs des autres versions (mode " "interactif)." -#: agenda_culturel/forms.py:448 +#: agenda_culturel/forms.py:449 msgid " Warning: a version is already locally modified." msgstr " Attention: une version a déjà été modifiée localement." -#: agenda_culturel/forms.py:453 +#: agenda_culturel/forms.py:454 msgid "Create a new version by merging (interactive mode)." msgstr "Créer une nouvelle version par fusion (mode interactif)." -#: agenda_culturel/forms.py:460 +#: agenda_culturel/forms.py:461 msgid "Make {} independent." msgstr "Rendre {} indépendant." -#: agenda_culturel/forms.py:462 +#: agenda_culturel/forms.py:463 msgid "Make all versions independent." msgstr "Rendre toutes les versions indépendantes." -#: agenda_culturel/forms.py:496 agenda_culturel/models.py:800 +#: agenda_culturel/forms.py:497 agenda_culturel/models.py:801 msgid "Event" msgstr "Événement" -#: agenda_culturel/forms.py:521 +#: agenda_culturel/forms.py:522 msgid "Value of the selected version" msgstr "Valeur de la version sélectionnée" -#: agenda_culturel/forms.py:523 agenda_culturel/forms.py:527 +#: agenda_culturel/forms.py:524 agenda_culturel/forms.py:528 msgid "Value of version {}" msgstr "Valeur de la version {}" -#: agenda_culturel/forms.py:676 +#: agenda_culturel/forms.py:677 msgid "Apply category {} to the event {}" msgstr "Appliquer la catégorie {} à l'événement {}" -#: agenda_culturel/forms.py:693 agenda_culturel/models.py:459 -#: agenda_culturel/models.py:2061 +#: agenda_culturel/forms.py:694 agenda_culturel/models.py:460 +#: agenda_culturel/models.py:2062 msgid "Place" msgstr "Lieu" -#: agenda_culturel/forms.py:695 +#: agenda_culturel/forms.py:696 msgid "Create a missing place" msgstr "Créer un lieu manquant" -#: agenda_culturel/forms.py:705 +#: agenda_culturel/forms.py:706 msgid "Add \"{}\" to the aliases of the place" msgstr "Ajouter « {} » aux alias du lieu" -#: agenda_culturel/forms.py:734 +#: agenda_culturel/forms.py:735 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 " @@ -288,8 +288,8 @@ msgstr "" #: agenda_culturel/models.py:57 agenda_culturel/models.py:105 #: agenda_culturel/models.py:186 agenda_culturel/models.py:403 -#: agenda_culturel/models.py:431 agenda_culturel/models.py:512 -#: agenda_culturel/models.py:1761 agenda_culturel/models.py:1839 +#: agenda_culturel/models.py:431 agenda_culturel/models.py:513 +#: agenda_culturel/models.py:1762 agenda_culturel/models.py:1840 msgid "Name" msgstr "Nom" @@ -349,8 +349,8 @@ msgstr "Catégories" msgid "Tag name" msgstr "Nom de l'étiquette" -#: agenda_culturel/models.py:191 agenda_culturel/models.py:442 -#: agenda_culturel/models.py:524 agenda_culturel/models.py:655 +#: agenda_culturel/models.py:191 agenda_culturel/models.py:443 +#: agenda_culturel/models.py:525 agenda_culturel/models.py:656 msgid "Description" msgstr "Description" @@ -455,22 +455,33 @@ msgid "Address of this place (without city name)" msgstr "Adresse de ce lieu (sans le nom de la ville)" #: agenda_culturel/models.py:438 +msgid "Postcode" +msgstr "Code postal" + +#: agenda_culturel/models.py:438 +msgid "" +"The post code is not displayed, but makes it easier to find an address when " +"you enter it." +msgstr "Le code postal ne sera pas affiché, mais facilite la recherche " +"d'adresse au moment de la saisie." + +#: agenda_culturel/models.py:439 msgid "City" msgstr "Ville" -#: agenda_culturel/models.py:438 +#: agenda_culturel/models.py:439 msgid "City name" msgstr "Nom de la ville" -#: agenda_culturel/models.py:443 +#: agenda_culturel/models.py:444 msgid "Description of the place, including accessibility." msgstr "Description du lieu, inclus l'accessibilité." -#: agenda_culturel/models.py:450 +#: agenda_culturel/models.py:451 msgid "Alternative names" msgstr "Noms alternatifs" -#: agenda_culturel/models.py:452 +#: agenda_culturel/models.py:453 msgid "" "Alternative names or addresses used to match a place with the free-form " "location of an event." @@ -478,31 +489,31 @@ 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:460 +#: agenda_culturel/models.py:461 msgid "Places" msgstr "Lieux" -#: agenda_culturel/models.py:512 +#: agenda_culturel/models.py:513 msgid "Organisation name" msgstr "Nom de l'organisme" -#: agenda_culturel/models.py:516 +#: agenda_culturel/models.py:517 msgid "Website" msgstr "Site internet" -#: agenda_culturel/models.py:517 +#: agenda_culturel/models.py:518 msgid "Website of the organisation" msgstr "Site internet de l'organisme" -#: agenda_culturel/models.py:525 +#: agenda_culturel/models.py:526 msgid "Description of the organisation." msgstr "Description de l'organisme" -#: agenda_culturel/models.py:532 +#: agenda_culturel/models.py:533 msgid "Principal place" msgstr "Lieu principal" -#: agenda_culturel/models.py:533 +#: agenda_culturel/models.py:534 msgid "" "Place mainly associated with this organizer. Mainly used if there is a " "similarity in the name, to avoid redundant displays." @@ -510,75 +521,75 @@ msgstr "" "Lieu principalement associé à cet organisateur. Principalement utilisé s'il " "y a une similarité de nom, pour éviter les affichages redondants." -#: agenda_culturel/models.py:540 +#: agenda_culturel/models.py:541 msgid "Organisation" msgstr "Organisme" -#: agenda_culturel/models.py:541 +#: agenda_culturel/models.py:542 msgid "Organisations" msgstr "Organismes" -#: agenda_culturel/models.py:553 agenda_culturel/models.py:1880 +#: agenda_culturel/models.py:554 agenda_culturel/models.py:1881 msgid "Published" msgstr "Publié" -#: agenda_culturel/models.py:554 +#: agenda_culturel/models.py:555 msgid "Draft" msgstr "Brouillon" -#: agenda_culturel/models.py:555 +#: agenda_culturel/models.py:556 msgid "Trash" msgstr "Corbeille" -#: agenda_culturel/models.py:564 +#: agenda_culturel/models.py:565 msgid "Author of the event creation" msgstr "Auteur de la création de l'événement" -#: agenda_culturel/models.py:572 +#: agenda_culturel/models.py:573 msgid "Author of the last importation" msgstr "Auteur de la dernière importation" -#: agenda_culturel/models.py:580 +#: agenda_culturel/models.py:581 msgid "Author of the last modification" msgstr "Auteur de la dernière modification" -#: agenda_culturel/models.py:588 +#: agenda_culturel/models.py:589 msgid "Author of the last moderation" msgstr "Auteur de la dernière modération" -#: agenda_culturel/models.py:599 +#: agenda_culturel/models.py:600 msgid "Title" msgstr "Titre" -#: agenda_culturel/models.py:603 agenda_culturel/models.py:1977 +#: agenda_culturel/models.py:604 agenda_culturel/models.py:1978 msgid "Status" msgstr "Status" -#: agenda_culturel/models.py:615 +#: agenda_culturel/models.py:616 msgid "Start day" msgstr "Date de début" -#: agenda_culturel/models.py:618 +#: agenda_culturel/models.py:619 msgid "Start time" msgstr "Heure de début" -#: agenda_culturel/models.py:624 +#: agenda_culturel/models.py:625 msgid "End day" msgstr "Date de fin" -#: agenda_culturel/models.py:629 +#: agenda_culturel/models.py:630 msgid "End time" msgstr "Heure de fin" -#: agenda_culturel/models.py:633 +#: agenda_culturel/models.py:634 msgid "Recurrence" msgstr "Récurrence" -#: agenda_culturel/models.py:644 +#: agenda_culturel/models.py:645 msgid "Location (free form)" msgstr "Localisation (forme libre)" -#: agenda_culturel/models.py:646 +#: agenda_culturel/models.py:647 msgid "" "Address of the event in case its not available in the already known places " "(free form)" @@ -586,11 +597,11 @@ 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:663 +#: agenda_culturel/models.py:664 msgid "Organisers" msgstr "Organisateurs" -#: agenda_culturel/models.py:665 +#: agenda_culturel/models.py:666 msgid "" "list of event organisers. Organizers will only be displayed if one of them " "does not normally use the venue." @@ -598,191 +609,191 @@ msgstr "" "Liste des organisateurs de l'événements. Les organisateurs seront affichés " "uniquement si au moins un d'entre eux n'utilise pas habituellement le lieu." -#: agenda_culturel/models.py:678 +#: agenda_culturel/models.py:679 msgid "Illustration (URL)" msgstr "Illustration (URL)" -#: agenda_culturel/models.py:679 +#: agenda_culturel/models.py:680 msgid "External URL of the illustration image" msgstr "URL externe de l'image illustrative" -#: agenda_culturel/models.py:685 +#: agenda_culturel/models.py:686 msgid "Illustration description" msgstr "Description de l'illustration" -#: agenda_culturel/models.py:686 +#: agenda_culturel/models.py:687 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:694 +#: agenda_culturel/models.py:695 msgid "Importation source" msgstr "Source d'importation" -#: agenda_culturel/models.py:695 +#: agenda_culturel/models.py:696 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:701 +#: agenda_culturel/models.py:702 msgid "UUIDs" msgstr "UUIDs" -#: agenda_culturel/models.py:702 +#: agenda_culturel/models.py:703 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:708 +#: agenda_culturel/models.py:709 msgid "URLs" msgstr "URLs" -#: agenda_culturel/models.py:709 +#: agenda_culturel/models.py:710 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:723 +#: agenda_culturel/models.py:724 msgid "Other versions" msgstr "" -#: agenda_culturel/models.py:801 +#: agenda_culturel/models.py:802 msgid "Events" msgstr "Événements" -#: agenda_culturel/models.py:1742 +#: agenda_culturel/models.py:1743 msgid "Contact message" msgstr "Message de contact" -#: agenda_culturel/models.py:1743 +#: agenda_culturel/models.py:1744 msgid "Contact messages" msgstr "Messages de contact" -#: agenda_culturel/models.py:1746 +#: agenda_culturel/models.py:1747 msgid "Subject" msgstr "Sujet" -#: agenda_culturel/models.py:1747 +#: agenda_culturel/models.py:1748 msgid "The subject of your message" msgstr "Sujet de votre message" -#: agenda_culturel/models.py:1753 +#: agenda_culturel/models.py:1754 msgid "Related event" msgstr "Événement associé" -#: agenda_culturel/models.py:1754 +#: agenda_culturel/models.py:1755 msgid "The message is associated with this event." msgstr "Le message est associé à cet événement." -#: agenda_culturel/models.py:1762 +#: agenda_culturel/models.py:1763 msgid "Your name" msgstr "Votre nom" -#: agenda_culturel/models.py:1768 +#: agenda_culturel/models.py:1769 msgid "Email address" msgstr "Adresse email" -#: agenda_culturel/models.py:1769 +#: agenda_culturel/models.py:1770 msgid "Your email address" msgstr "Votre adresse email" -#: agenda_culturel/models.py:1774 +#: agenda_culturel/models.py:1775 msgid "Message" msgstr "Message" -#: agenda_culturel/models.py:1774 +#: agenda_culturel/models.py:1775 msgid "Your message" msgstr "Votre message" -#: agenda_culturel/models.py:1780 +#: agenda_culturel/models.py:1781 msgid "This message is a spam." msgstr "Ce message est un spam." -#: agenda_culturel/models.py:1787 +#: agenda_culturel/models.py:1788 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:1792 +#: agenda_culturel/models.py:1793 msgid "Comments" msgstr "Commentaires" -#: agenda_culturel/models.py:1793 +#: agenda_culturel/models.py:1794 msgid "Comments on the message from the moderation team" msgstr "Commentaires sur ce message par l'équipe de modération" -#: agenda_culturel/models.py:1808 agenda_culturel/models.py:1957 +#: agenda_culturel/models.py:1809 agenda_culturel/models.py:1958 msgid "Recurrent import" msgstr "Import récurrent" -#: agenda_culturel/models.py:1809 +#: agenda_culturel/models.py:1810 msgid "Recurrent imports" msgstr "Imports récurrents" -#: agenda_culturel/models.py:1813 +#: agenda_culturel/models.py:1814 msgid "ical" msgstr "ical" -#: agenda_culturel/models.py:1814 +#: agenda_culturel/models.py:1815 msgid "ical no busy" msgstr "ical sans busy" -#: agenda_culturel/models.py:1815 +#: agenda_culturel/models.py:1816 msgid "ical no VC" msgstr "ical sans VC" -#: agenda_culturel/models.py:1816 +#: agenda_culturel/models.py:1817 msgid "lacoope.org" msgstr "lacoope.org" -#: agenda_culturel/models.py:1817 +#: agenda_culturel/models.py:1818 msgid "la comédie" msgstr "la comédie" -#: agenda_culturel/models.py:1818 +#: agenda_culturel/models.py:1819 msgid "le fotomat" msgstr "le fotomat" -#: agenda_culturel/models.py:1819 +#: agenda_culturel/models.py:1820 msgid "la puce à l'oreille" msgstr "la puce à loreille" -#: agenda_culturel/models.py:1820 +#: agenda_culturel/models.py:1821 msgid "Plugin wordpress MEC" msgstr "Plugin wordpress MEC" -#: agenda_culturel/models.py:1821 +#: agenda_culturel/models.py:1822 msgid "Événements d'une page FB" msgstr "Événements d'une page FB" -#: agenda_culturel/models.py:1822 +#: agenda_culturel/models.py:1823 msgid "la cour des 3 coquins" msgstr "la cour des 3 coquins" -#: agenda_culturel/models.py:1823 +#: agenda_culturel/models.py:1824 msgid "Arachnée concert" msgstr "Arachnée concert" -#: agenda_culturel/models.py:1824 +#: agenda_culturel/models.py:1825 msgid "Le Rio" msgstr "Le Rio" -#: agenda_culturel/models.py:1827 +#: agenda_culturel/models.py:1828 msgid "simple" msgstr "simple" -#: agenda_culturel/models.py:1828 +#: agenda_culturel/models.py:1829 msgid "Headless Chromium" msgstr "chromium sans interface" -#: agenda_culturel/models.py:1829 +#: agenda_culturel/models.py:1830 msgid "Headless Chromium (pause)" msgstr "chromium sans interface (pause)" -#: agenda_culturel/models.py:1834 +#: agenda_culturel/models.py:1835 msgid "daily" msgstr "chaque jour" -#: agenda_culturel/models.py:1836 +#: agenda_culturel/models.py:1837 msgid "weekly" msgstr "chaque semaine" -#: agenda_culturel/models.py:1841 +#: agenda_culturel/models.py:1842 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." @@ -790,143 +801,143 @@ 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:1848 +#: agenda_culturel/models.py:1849 msgid "Processor" msgstr "Processeur" -#: agenda_culturel/models.py:1851 +#: agenda_culturel/models.py:1852 msgid "Downloader" msgstr "Téléchargeur" -#: agenda_culturel/models.py:1858 +#: agenda_culturel/models.py:1859 msgid "Import recurrence" msgstr "Récurrence d'import" -#: agenda_culturel/models.py:1865 +#: agenda_culturel/models.py:1866 msgid "Source" msgstr "Source" -#: agenda_culturel/models.py:1866 +#: agenda_culturel/models.py:1867 msgid "URL of the source document" msgstr "URL du document source" -#: agenda_culturel/models.py:1870 +#: agenda_culturel/models.py:1871 msgid "Browsable url" msgstr "URL navigable" -#: agenda_culturel/models.py:1872 +#: agenda_culturel/models.py:1873 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:1881 +#: agenda_culturel/models.py:1882 msgid "Status of each imported event (published or draft)" msgstr "Status de chaque événement importé (publié ou brouillon)" -#: agenda_culturel/models.py:1886 +#: agenda_culturel/models.py:1887 msgid "Address for each imported event" msgstr "Adresse de chaque événement importé" -#: agenda_culturel/models.py:1894 +#: agenda_culturel/models.py:1895 msgid "Organiser" msgstr "Organisateur" -#: agenda_culturel/models.py:1895 +#: agenda_culturel/models.py:1896 msgid "Organiser of each imported event" msgstr "Organisateur de chaque événement importé" -#: agenda_culturel/models.py:1905 +#: agenda_culturel/models.py:1906 msgid "Category of each imported event" msgstr "Catégorie de chaque événement importé" -#: agenda_culturel/models.py:1913 +#: agenda_culturel/models.py:1914 msgid "Tags for each imported event" msgstr "Étiquettes de chaque événement importé" -#: agenda_culturel/models.py:1914 +#: agenda_culturel/models.py:1915 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:1943 +#: agenda_culturel/models.py:1944 msgid "Running" msgstr "En cours" -#: agenda_culturel/models.py:1944 +#: agenda_culturel/models.py:1945 msgid "Canceled" msgstr "Annulé" -#: agenda_culturel/models.py:1945 +#: agenda_culturel/models.py:1946 msgid "Success" msgstr "Succès" -#: agenda_culturel/models.py:1946 +#: agenda_culturel/models.py:1947 msgid "Failed" msgstr "Erreur" -#: agenda_culturel/models.py:1949 +#: agenda_culturel/models.py:1950 msgid "Batch importation" msgstr "Importation par lot" -#: agenda_culturel/models.py:1950 +#: agenda_culturel/models.py:1951 msgid "Batch importations" msgstr "Importations par lot" -#: agenda_culturel/models.py:1958 +#: agenda_culturel/models.py:1959 msgid "Reference to the recurrent import processing" msgstr "Référence du processus d'import récurrent" -#: agenda_culturel/models.py:1966 +#: agenda_culturel/models.py:1967 msgid "URL (if not recurrent import)" msgstr "URL (si pas d'import récurrent)" -#: agenda_culturel/models.py:1968 +#: agenda_culturel/models.py:1969 msgid "Source URL if no RecurrentImport is associated." msgstr "URL source si aucun import récurrent n'est associé" -#: agenda_culturel/models.py:1981 +#: agenda_culturel/models.py:1982 msgid "Error message" msgstr "Votre message" -#: agenda_culturel/models.py:1985 +#: agenda_culturel/models.py:1986 msgid "Number of collected events" msgstr "Nombre d'événements collectés" -#: agenda_culturel/models.py:1988 +#: agenda_culturel/models.py:1989 msgid "Number of imported events" msgstr "Nombre d'événements importés" -#: agenda_culturel/models.py:1991 +#: agenda_culturel/models.py:1992 msgid "Number of updated events" msgstr "Nombre d'événements mis à jour" -#: agenda_culturel/models.py:1994 +#: agenda_culturel/models.py:1995 msgid "Number of removed events" msgstr "Nombre d'événements supprimés" -#: agenda_culturel/models.py:2002 +#: agenda_culturel/models.py:2003 msgid "Weight" msgstr "Poids" -#: agenda_culturel/models.py:2003 +#: agenda_culturel/models.py:2004 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:2010 +#: agenda_culturel/models.py:2011 msgid "Category applied to the event" msgstr "Catégorie appliquée à l'événement" -#: agenda_culturel/models.py:2015 +#: agenda_culturel/models.py:2016 msgid "Contained in the title" msgstr "Contenu dans le titre" -#: agenda_culturel/models.py:2016 +#: agenda_culturel/models.py:2017 msgid "Text contained in the event title" msgstr "Texte contenu dans le titre de l'événement" -#: agenda_culturel/models.py:2022 +#: agenda_culturel/models.py:2023 msgid "Exact title extract" msgstr "Extrait exact du titre" -#: agenda_culturel/models.py:2024 +#: agenda_culturel/models.py:2025 msgid "" "If checked, the extract will be searched for in the title using the exact " "form (capitals, accents)." @@ -934,19 +945,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans le titre en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2030 +#: agenda_culturel/models.py:2031 msgid "Contained in the description" msgstr "Contenu dans la description" -#: agenda_culturel/models.py:2031 +#: agenda_culturel/models.py:2032 msgid "Text contained in the description" msgstr "Texte contenu dans la description" -#: agenda_culturel/models.py:2037 +#: agenda_culturel/models.py:2038 msgid "Exact description extract" msgstr "Extrait exact de description" -#: agenda_culturel/models.py:2039 +#: agenda_culturel/models.py:2040 msgid "" "If checked, the extract will be searched for in the description using the " "exact form (capitals, accents)." @@ -954,19 +965,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans la description en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2045 +#: agenda_culturel/models.py:2046 msgid "Contained in the location" msgstr "Contenu dans la localisation" -#: agenda_culturel/models.py:2046 +#: agenda_culturel/models.py:2047 msgid "Text contained in the event location" msgstr "Texte contenu dans la localisation de l'événement" -#: agenda_culturel/models.py:2052 +#: agenda_culturel/models.py:2053 msgid "Exact location extract" msgstr "Extrait exact de localisation" -#: agenda_culturel/models.py:2054 +#: agenda_culturel/models.py:2055 msgid "" "If checked, the extract will be searched for in the location using the exact " "form (capitals, accents)." @@ -974,15 +985,15 @@ msgstr "" "Si coché, l'extrait sera recherché dans la localisation en utilisant la " "forme exacte (majuscules, accents)" -#: agenda_culturel/models.py:2062 +#: agenda_culturel/models.py:2063 msgid "Location from place" msgstr "Localisation depuis le lieu" -#: agenda_culturel/models.py:2071 +#: agenda_culturel/models.py:2072 msgid "Categorisation rule" msgstr "Règle de catégorisation" -#: agenda_culturel/models.py:2072 +#: agenda_culturel/models.py:2073 msgid "Categorisation rules" msgstr "Règles de catégorisation" @@ -990,27 +1001,27 @@ msgstr "Règles de catégorisation" msgid "French" msgstr "français" -#: agenda_culturel/views.py:150 +#: agenda_culturel/views.py:151 msgid "Recurrent import name" msgstr "Nome de l'import récurrent" -#: agenda_culturel/views.py:151 +#: agenda_culturel/views.py:152 msgid "Add another" msgstr "Ajouter un autre" -#: agenda_culturel/views.py:152 +#: agenda_culturel/views.py:153 msgid "Browse..." msgstr "Naviguer..." -#: agenda_culturel/views.py:153 +#: agenda_culturel/views.py:154 msgid "No file selected." msgstr "Pas de fichier sélectionné." -#: agenda_culturel/views.py:289 +#: agenda_culturel/views.py:290 msgid "The static content has been successfully updated." msgstr "Le contenu statique a été modifié avec succès." -#: agenda_culturel/views.py:297 +#: agenda_culturel/views.py:298 msgid "" "The event cannot be updated because the import process is not available for " "the referenced sources." @@ -1018,33 +1029,33 @@ msgstr "" "La mise à jour de l'événement n'est pas possible car le processus d'import " "n'est pas disponible pour les sources référencées." -#: agenda_culturel/views.py:300 +#: agenda_culturel/views.py:301 msgid "The event update has been queued and will be completed shortly." msgstr "" "La mise à jour de l'événement a été mise en attente et sera effectuée sous " "peu." -#: agenda_culturel/views.py:310 +#: 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:361 +#: agenda_culturel/views.py:362 msgid "The event {} has been moderated with success." -msgstr "L'événement {} a été modéré avec succès. +msgstr "L'événement {} a été modéré avec succès." -#: agenda_culturel/views.py:466 +#: agenda_culturel/views.py:467 msgid "The event has been successfully deleted." msgstr "L'événement a été supprimé avec succès." -#: agenda_culturel/views.py:498 +#: agenda_culturel/views.py:499 msgid "The status has been successfully modified." msgstr "Le status a été modifié avec succès." -#: agenda_culturel/views.py:532 +#: agenda_culturel/views.py:533 msgid "The event was created: {}." msgstr "L'événement a été créé: {}." -#: agenda_culturel/views.py:534 +#: agenda_culturel/views.py:535 msgid "" "The event has been submitted and will be published as soon as it has been " "validated by the moderation team." @@ -1052,82 +1063,82 @@ 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:628 agenda_culturel/views.py:680 +#: agenda_culturel/views.py:629 agenda_culturel/views.py:681 msgid "{} has not been submitted since its already known: {}." msgstr "{} n'a pas été soumis car il est déjà connu: {}." -#: agenda_culturel/views.py:633 agenda_culturel/views.py:686 +#: agenda_culturel/views.py:634 agenda_culturel/views.py:687 msgid "" "{} has not been submitted since its already known and currently into " "moderation process." msgstr "{} n'a pas été soumis car il est déjà connu et en cours de modération" -#: agenda_culturel/views.py:643 +#: agenda_culturel/views.py:644 msgid "Integrating {} url(s) into our import process." msgstr "Intégration de {} url(s) dans notre processus d'import." -#: agenda_culturel/views.py:693 +#: agenda_culturel/views.py:694 msgid "Integrating {} into our import process." msgstr "Intégration de {} dans notre processus d'import." -#: agenda_culturel/views.py:748 +#: agenda_culturel/views.py:749 msgid "Your message has been sent successfully." msgstr "Votre message a été envoyé avec succès." -#: agenda_culturel/views.py:769 +#: agenda_culturel/views.py:770 msgid "Reporting the event {} on {}" msgstr "Signaler l'événement {} du {}" -#: agenda_culturel/views.py:779 +#: agenda_culturel/views.py:780 msgid "The contact message has been successfully deleted." msgstr "Le message de contact a été supprimé avec succès." -#: agenda_culturel/views.py:793 +#: agenda_culturel/views.py:794 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:930 +#: agenda_culturel/views.py:931 msgid "Spam has been successfully deleted." msgstr "Le spam a été supprimé avec succès" -#: agenda_culturel/views.py:1053 +#: agenda_culturel/views.py:1054 msgid "The import has been run successfully." msgstr "L'import a été lancé avec succès" -#: agenda_culturel/views.py:1072 +#: agenda_culturel/views.py:1073 msgid "The import has been canceled." msgstr "L'import a été annulé" -#: agenda_culturel/views.py:1150 +#: agenda_culturel/views.py:1151 msgid "The recurrent import has been successfully modified." msgstr "L'import récurrent a été modifié avec succès." -#: agenda_culturel/views.py:1159 +#: agenda_culturel/views.py:1160 msgid "The recurrent import has been successfully deleted." msgstr "L'import récurrent a été supprimé avec succès" -#: agenda_culturel/views.py:1199 +#: agenda_culturel/views.py:1200 msgid "The import has been launched." msgstr "L'import a été lancé" -#: agenda_culturel/views.py:1221 +#: agenda_culturel/views.py:1222 msgid "Imports has been launched." msgstr "Les imports ont été lancés" -#: agenda_culturel/views.py:1283 +#: agenda_culturel/views.py:1284 msgid "Update successfully completed." msgstr "Mise à jour réalisée avec succès." -#: agenda_culturel/views.py:1344 +#: agenda_culturel/views.py:1345 msgid "Creation of a merged event has been successfully completed." msgstr "Création d'un événement fusionné réalisée avec succès." -#: agenda_culturel/views.py:1380 +#: agenda_culturel/views.py:1381 msgid "Events have been marked as unduplicated." msgstr "Les événements ont été marqués comme non dupliqués." -#: agenda_culturel/views.py:1394 agenda_culturel/views.py:1403 -#: agenda_culturel/views.py:1421 +#: agenda_culturel/views.py:1395 agenda_culturel/views.py:1404 +#: agenda_culturel/views.py:1422 msgid "" "The selected item is no longer included in the list of duplicates. Someone " "else has probably modified the list in the meantime." @@ -1135,23 +1146,23 @@ msgstr "" "L'élément sélectionné ne fait plus partie de la liste des dupliqués. Une " "autre personne a probablement modifié la liste entre temps." -#: agenda_culturel/views.py:1397 +#: agenda_culturel/views.py:1398 msgid "The selected event has been set as representative" msgstr "L'événement sélectionné a été défini comme representatif." -#: agenda_culturel/views.py:1412 +#: agenda_culturel/views.py:1413 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:1456 +#: agenda_culturel/views.py:1457 msgid "Cleaning up duplicates: {} item(s) fixed." msgstr "Nettoyage des dupliqués: {} élément(s) corrigé(s)." -#: agenda_culturel/views.py:1505 +#: agenda_culturel/views.py:1506 msgid "The event was successfully duplicated." msgstr "L'événement a été marqué dupliqué avec succès." -#: agenda_culturel/views.py:1513 +#: agenda_culturel/views.py:1514 msgid "" "The event has been successfully flagged as a duplicate. The moderation team " "will deal with your suggestion shortly." @@ -1159,32 +1170,32 @@ 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:1566 +#: agenda_culturel/views.py:1567 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:1575 +#: agenda_culturel/views.py:1576 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:1597 +#: agenda_culturel/views.py:1598 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:1604 +#: agenda_culturel/views.py:1605 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:1611 agenda_culturel/views.py:1664 +#: agenda_culturel/views.py:1612 agenda_culturel/views.py:1665 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:1650 +#: agenda_culturel/views.py:1651 msgid "" "The rules were successfully applied and 1 event with default category was " "categorised." @@ -1192,7 +1203,7 @@ msgstr "" "Les règles ont été appliquées avec succès et 1 événement avec catégorie par " "défaut a été catégorisé" -#: agenda_culturel/views.py:1657 +#: agenda_culturel/views.py:1658 msgid "" "The rules were successfully applied and {} events with default category were " "categorised." @@ -1200,58 +1211,58 @@ msgstr "" "Les règles ont été appliquées avec succès et {} événements avec catégorie " "par défaut ont été catégorisés" -#: agenda_culturel/views.py:1749 agenda_culturel/views.py:1811 -#: agenda_culturel/views.py:1849 +#: agenda_culturel/views.py:1750 agenda_culturel/views.py:1812 +#: agenda_culturel/views.py:1850 msgid "{} events have been updated." msgstr "{} événements ont été mis à jour." -#: agenda_culturel/views.py:1752 agenda_culturel/views.py:1813 -#: agenda_culturel/views.py:1852 +#: agenda_culturel/views.py:1753 agenda_culturel/views.py:1814 +#: agenda_culturel/views.py:1853 msgid "1 event has been updated." msgstr "1 événement a été mis à jour" -#: agenda_culturel/views.py:1754 agenda_culturel/views.py:1815 -#: agenda_culturel/views.py:1854 +#: agenda_culturel/views.py:1755 agenda_culturel/views.py:1816 +#: agenda_culturel/views.py:1855 msgid "No events have been modified." msgstr "Aucun événement n'a été modifié." -#: agenda_culturel/views.py:1763 +#: agenda_culturel/views.py:1764 msgid "The place has been successfully updated." msgstr "Le lieu a été modifié avec succès." -#: agenda_culturel/views.py:1772 +#: agenda_culturel/views.py:1773 msgid "The place has been successfully created." msgstr "Le lieu a été créé avec succès." -#: agenda_culturel/views.py:1837 +#: agenda_culturel/views.py:1838 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:1841 +#: agenda_culturel/views.py:1842 msgid "A new alias has been added to the selected place." msgstr "Un nouvel alias a été créé pour le lieu sélectionné." -#: agenda_culturel/views.py:1934 +#: agenda_culturel/views.py:1944 msgid "The organisation has been successfully updated." msgstr "L'organisme a été modifié avec succès." -#: agenda_culturel/views.py:1943 +#: agenda_culturel/views.py:1953 msgid "The organisation has been successfully created." msgstr "L'organisme a été créé avec succès." -#: agenda_culturel/views.py:1960 +#: agenda_culturel/views.py:1970 msgid "The tag has been successfully updated." msgstr "L'étiquette a été modifiée avec succès." -#: agenda_culturel/views.py:1967 +#: agenda_culturel/views.py:1977 msgid "The tag has been successfully created." msgstr "L'étiquette a été créée avec succès." -#: agenda_culturel/views.py:2031 +#: agenda_culturel/views.py:2041 msgid "You have not modified the tag name." msgstr "Vous n'avez pas modifié le nom de l'étiquette." -#: agenda_culturel/views.py:2041 +#: agenda_culturel/views.py:2051 msgid "" "This tag {} is already in use, and is described by different information " "from the current tag. You can force renaming by checking the corresponding " @@ -1264,7 +1275,7 @@ msgstr "" "sera supprimée, et tous les événements associés à l'étiquette {} seront " "associés à l'étiquette {}." -#: agenda_culturel/views.py:2048 +#: agenda_culturel/views.py:2058 msgid "" "This tag {} is already in use. You can force renaming by checking the " "corresponding option." @@ -1272,11 +1283,10 @@ msgstr "" "Cette étiquette {} est déjà utilisée. Vous pouvez forcer le renommage en " "cochant l'option correspondante." -#: agenda_culturel/views.py:2082 +#: agenda_culturel/views.py:2092 msgid "The tag {} has been successfully renamed to {}." msgstr "L'étiquette {} a été renommée avec succès en {}." -#: agenda_culturel/views.py:2120 +#: agenda_culturel/views.py:2130 msgid "The tag {} has been successfully deleted." msgstr "L'événement {} a été supprimé avec succès." - From e050ce5edae79fa4093b49a1ab54c5924cb060b0 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sat, 7 Dec 2024 10:11:36 +0100 Subject: [PATCH 33/85] =?UTF-8?q?On=20d=C3=A9sactive=20la=20sortie=20d'err?= =?UTF-8?q?eurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../import_tasks/custom_extractors/fbevents.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py b/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py index bf02a96..d6be501 100644 --- a/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py +++ b/src/agenda_culturel/import_tasks/custom_extractors/fbevents.py @@ -19,6 +19,8 @@ class CExtractor(TwoStepsExtractor): def build_event_url_list(self, content): soup = BeautifulSoup(content, "html.parser") + debug = False + found = False links = soup.find_all("a") for link in links: @@ -26,7 +28,7 @@ class CExtractor(TwoStepsExtractor): self.add_event_url(link.get('href').split('?')[0]) found = True - if not found: + if not found and debug: directory = "errors/" if not os.path.exists(directory): os.makedirs(directory) From cd52ae02868d1b8046441a3eaad000fa5b8ecf21 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sat, 7 Dec 2024 11:15:56 +0100 Subject: [PATCH 34/85] Ajout d'une ligne "maintenant" Fix #235 --- src/agenda_culturel/calendar.py | 9 +++++++++ src/agenda_culturel/settings/base.py | 2 +- src/agenda_culturel/static/style.scss | 17 +++++++++++++++++ .../templates/agenda_culturel/page-week.html | 3 +++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/calendar.py b/src/agenda_culturel/calendar.py index a62e9ac..e9df9ff 100644 --- a/src/agenda_culturel/calendar.py +++ b/src/agenda_culturel/calendar.py @@ -117,6 +117,15 @@ class DayInCalendar: if e.start_time is None else e.start_time ) + if self.is_today(): + now = timezone.now() + nday = now.date() + ntime = now.time() + for idx,e in enumerate(self.events): + if (nday < e.start_day) or (nday == e.start_day and e.start_time and ntime <= e.start_time): + self.events[idx].is_first_after_now = True + break + def events_by_category_ordered(self): from .models import Category diff --git a/src/agenda_culturel/settings/base.py b/src/agenda_culturel/settings/base.py index 646507b..f00aef2 100644 --- a/src/agenda_culturel/settings/base.py +++ b/src/agenda_culturel/settings/base.py @@ -148,7 +148,7 @@ TIME_ZONE = "Europe/Paris" USE_I18N = True -USE_TZ = True +USE_TZ = False LANGUAGES = ( ("fr", _("French")), diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index 260b4ee..f5abd0a 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -329,6 +329,7 @@ footer [data-tooltip] { scroll-behavior: smooth; transition-duration: 200ms; + .cat { margin-right: 0; } @@ -1551,4 +1552,20 @@ label.required::after { grid-column: 1 / 3; } } +} + +.line-now { + font-size: 60%; + div { + display: grid; + grid-template-columns: fit-content(2em) auto; + column-gap: .2em; + color: red; + .line { + margin-top: .6em; + border-top: 1px solid red; + } + } + margin-bottom: 0; + } \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/page-week.html b/src/agenda_culturel/templates/agenda_culturel/page-week.html index c078f32..2f1db3a 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-week.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-week.html @@ -85,6 +85,9 @@ {% if day.events %}
    {% for event in day.events %} + {% if event.is_first_after_now %} +
  • {% now "H:i" %}
  • + {% endif %}
  • {{ event.category | circle_cat:event.has_recurrences }} {% if event.start_day == day.date and event.start_time %} {{ event.start_time }} From 92da6585c67ab1d53d58d5823516017cc0013438 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 09:07:16 +0100 Subject: [PATCH 35/85] =?UTF-8?q?Correction=20apr=C3=A8s=20modification=20?= =?UTF-8?q?de=20USE=5FTZ=3DFalse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #245 --- src/agenda_culturel/forms.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index 30e3e85..fc272db 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -33,7 +33,6 @@ from django.utils.translation import gettext_lazy as _ from string import ascii_uppercase as auc from .templatetags.utils_extra import int_to_abc from django.utils.safestring import mark_safe -from django.utils.timezone import localtime from django.utils.formats import localize from .templatetags.event_extra import event_field_verbose_name, field_to_html import os @@ -549,17 +548,17 @@ class MergeDuplicates(Form): '
  • ' + e.title + "
  • " ) result += ( - "
  • Création : " + localize(localtime(e.created_date)) + "
  • " + "
  • Création : " + localize(e.created_date) + "
  • " ) result += ( "
  • Dernière modification : " - + localize(localtime(e.modified_date)) + + localize(e.modified_date) + "
  • " ) if e.imported_date: result += ( "
  • Dernière importation : " - + localize(localtime(e.imported_date)) + + localize(e.imported_date) + "
  • " ) result += "
" From 14e25b660cc032911ceb79730c2cce0fe3fe911a Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 09:21:30 +0100 Subject: [PATCH 36/85] Ajustement position ligne rouge --- src/agenda_culturel/static/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index f5abd0a..242bbca 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -1562,7 +1562,7 @@ label.required::after { column-gap: .2em; color: red; .line { - margin-top: .6em; + margin-top: .7em; border-top: 1px solid red; } } From 02448cf4d47890193be233c551b25a3743ea35a7 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 09:25:19 +0100 Subject: [PATCH 37/85] Fix export ical --- src/agenda_culturel/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 510c06b..1e35bb8 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1700,8 +1700,9 @@ class Event(models.Model): eventIcal.add("summary", event.title) eventIcal.add("name", event.title) url = ("\n" + event.reference_urls[0]) if event.reference_urls and len(event.reference_urls) > 0 else "" + description = event.description if event.description else "" eventIcal.add( - "description", event.description + url + "description", description + url ) eventIcal.add("location", event.exact_location or event.location) From 70ae92854fad79ea2818c738cbadd2b0d107efc5 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 15:08:26 +0100 Subject: [PATCH 38/85] Consolidate migration --- .../migrations/0081_auto_20241010_2235.py | 11 ++++++----- src/agenda_culturel/views.py | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/agenda_culturel/migrations/0081_auto_20241010_2235.py b/src/agenda_culturel/migrations/0081_auto_20241010_2235.py index 1f6e475..fcc4fd3 100644 --- a/src/agenda_culturel/migrations/0081_auto_20241010_2235.py +++ b/src/agenda_culturel/migrations/0081_auto_20241010_2235.py @@ -1,11 +1,11 @@ # Generated by Django 4.2.9 on 2024-10-10 20:35 from django.db import migrations -from agenda_culturel.models import Place from django.contrib.gis.geos import Point def change_coord_format(apps, schema_editor): - places = Place.objects.all() + Place = apps.get_model("agenda_culturel", "Place") + places = Place.objects.values("location", "location_pt").all() for p in places: l = p.location.split(',') @@ -13,14 +13,15 @@ def change_coord_format(apps, schema_editor): p.location_pt = Point(float(l[1]), float(l[0])) else: p.location_pt = Point(3.08333, 45.783329) - p.save() + p.save(update_fields=["location_pt"]) def reverse_coord_format(apps, schema_editor): - places = Place.objects.all() + Place = apps.get_model("agenda_culturel", "Place") + places = Place.objects.values("location", "location_pt").all() for p in places: p.location = ','.join([p.location_pt[1], p.location_pt[0]]) - p.save() + p.save(update_fields=["location"]) diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index 27eed27..4ced3d8 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -721,7 +721,6 @@ def export_event_ical(request, year, month, day, pk): return response - def export_ical(request): now = date.today() From ced15d5113ac1a17e9a2daf5ecb4081ad6577354 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 15:53:28 +0100 Subject: [PATCH 39/85] On assure que les dumps contiennent les utilisateurs --- README.md | 2 +- scripts/reset-database.sh | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c98d408..98ae204 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Pour ajouter une nouvelle source custom: ### Récupérer un dump du prod sur un serveur dev * sur le serveur de dev: - * ```docker exec -i agenda_culturel-backend python3 manage.py dumpdata --format=json --exclude=admin.logentry --exclude=auth.group --exclude=auth.permission --exclude=auth.user --exclude=contenttypes --indent=2 > fixtures/postgres-backup-20241101.json``` (à noter qu'ici on oublie les comptes, qu'il faudra recréer) + * ```docker exec -i agenda_culturel-backend python3 manage.py dumpdata --natural-foreign --natural-primary --format=json --exclude=admin.logentry --indent=2 > fixtures/postgres-backup-20241101.json``` (à noter qu'ici on oublie les comptes, qu'il faudra recréer) * sur le serveur de prod: * On récupère le dump json ```scp $SERVEUR:$PATH/fixtures/postgres-backup-20241101.json src/fixtures/``` * ```scripts/reset-database.sh FIXTURE COMMIT``` où ```FIXTURE``` est le timestamp dans le nom de la fixture, et ```COMMIT``` est l'ID du commit git correspondant à celle en prod sur le serveur au moment de la création de la fixture diff --git a/scripts/reset-database.sh b/scripts/reset-database.sh index a33b74c..23afac3 100755 --- a/scripts/reset-database.sh +++ b/scripts/reset-database.sh @@ -73,6 +73,10 @@ git checkout $COMMIT echobold "Setup database stucture according to the selected commit" docker exec -i agenda_culturel-backend python3 manage.py migrate agenda_culturel +# remove all elements in database +echobold "Flush database" +docker exec -i agenda_culturel-backend python3 manage.py flush --no-input + # import data echobold "Import data" docker exec -i agenda_culturel-backend python3 manage.py loaddata --format=json $FFILE @@ -85,7 +89,4 @@ git checkout main echobold "Update database" docker exec -i agenda_culturel-backend python3 manage.py migrate agenda_culturel -# create superuser -echobold "Create superuser" -docker exec -ti agenda_culturel-backend python3 manage.py createsuperuser From f7f8d9cb0cee17757c63be4c801388669a96707f Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 16:34:41 +0100 Subject: [PATCH 40/85] On consolide la recherche (erreur 500 des moteurs de recherche) --- src/agenda_culturel/filters.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/filters.py b/src/agenda_culturel/filters.py index 07c23e8..a5d9d57 100644 --- a/src/agenda_culturel/filters.py +++ b/src/agenda_culturel/filters.py @@ -137,7 +137,11 @@ class EventFilter(django_filters.FilterSet): if self.get_cleaned_data("position") is None or self.get_cleaned_data("radius") is None: return parent d = self.get_cleaned_data("radius") - p = self.get_cleaned_data("position").location + p = self.get_cleaned_data("position") + if not isinstance(d, str) or not isinstance(p, ReferenceLocation): + return parent + p = p.location + return parent.exclude(exact_location=False).filter(exact_location__location__distance_lt=(p, D(km=d))) def get_url(self): @@ -188,6 +192,7 @@ class EventFilter(django_filters.FilterSet): def get_cleaned_data(self, name): + try: return self.form.cleaned_data[name] except AttributeError: From 3316d28e09e46e946244e51a166f23dc402d6368 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 17:32:46 +0100 Subject: [PATCH 41/85] =?UTF-8?q?Am=C3=A9lioration=20export=20ical:=20-=20?= =?UTF-8?q?ajout=20des=20images=20-=20ajout=20de=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/calendar.py | 4 ++-- src/agenda_culturel/models.py | 4 +++- src/agenda_culturel/views.py | 12 +++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/agenda_culturel/calendar.py b/src/agenda_culturel/calendar.py index e9df9ff..c80a922 100644 --- a/src/agenda_culturel/calendar.py +++ b/src/agenda_culturel/calendar.py @@ -300,10 +300,10 @@ class CalendarList: def time_intervals_list_first(self): return self.time_intervals_list(True) - def export_to_ics(self): + def export_to_ics(self, request): from .models import Event events = [event for day in self.get_calendar_days().values() for event in day.events] - return Event.export_to_ics(events) + return Event.export_to_ics(events, request) class CalendarMonth(CalendarList): diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 1e35bb8..cbaa76f 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1646,7 +1646,7 @@ class Event(models.Model): return (dtstart <= e_dtstart <= dtend) or (e_dtstart <= dtstart <= e_dtend) - def export_to_ics(events): + def export_to_ics(events, request): cal = icalCal() # Some properties are required to be compliant cal.add("prodid", "-//My calendar product//example.com//") @@ -1704,6 +1704,8 @@ class Event(models.Model): eventIcal.add( "description", description + url ) + if not event.local_image is None: + eventIcal.add('image', request.build_absolute_uri(event.local_image), parameters={'VALUE': 'URI'}) eventIcal.add("location", event.exact_location or event.location) cal.add_component(eventIcal) diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index 4ced3d8..62ff3ee 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -13,6 +13,8 @@ from django.utils.safestring import mark_safe from django.utils.decorators import method_decorator from honeypot.decorators import check_honeypot from .utils import PlaceGuesser +import hashlib +from django.core.cache import cache from django.contrib.gis.geos import Point @@ -711,7 +713,7 @@ def export_event_ical(request, year, month, day, pk): events = list() events.append(event) - cal = Event.export_to_ics(events) + cal = Event.export_to_ics(events, request) response = HttpResponse(content_type="text/calendar") response.content = cal.to_ical().decode("utf-8").replace("\r\n", "\n") @@ -726,8 +728,12 @@ def export_ical(request): request = EventFilter.set_default_values(request) filter = EventFilter(request.GET, queryset=get_event_qs(request), request=request) - calendar = CalendarList(now + timedelta(days=-7), now + timedelta(days=+60), filter) - ical = calendar.export_to_ics() + id_cache = hashlib.md5(filter.get_url().encode("utf8")).hexdigest() + ical = cache.get(id_cache) + if not ical: + calendar = CalendarList(now + timedelta(days=-7), now + timedelta(days=+60), filter) + ical = calendar.export_to_ics(request) + cache.set(id_cache, ical, 3600) # 1 heure response = HttpResponse(content_type="text/calendar") response.content = ical.to_ical().decode("utf-8").replace("\r\n", "\n") From 41d6b39988dc2e825f14f005f8737f90b189eec9 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 17:36:57 +0100 Subject: [PATCH 42/85] =?UTF-8?q?Fix=20=C3=A9v=C3=A9nements=20sans=20image?= =?UTF-8?q?=20dans=20l'ical?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index cbaa76f..dcc5b62 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1704,7 +1704,7 @@ class Event(models.Model): eventIcal.add( "description", description + url ) - if not event.local_image is None: + if not event.local_image is None and event.local_image != "": eventIcal.add('image', request.build_absolute_uri(event.local_image), parameters={'VALUE': 'URI'}) eventIcal.add("location", event.exact_location or event.location) From dd0c037929327c45f873afd24a3d8fcccf8be4ce Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 22:35:50 +0100 Subject: [PATCH 43/85] Description de l'ical --- src/agenda_culturel/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index dcc5b62..d405bd6 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1649,7 +1649,7 @@ class Event(models.Model): def export_to_ics(events, request): cal = icalCal() # Some properties are required to be compliant - cal.add("prodid", "-//My calendar product//example.com//") + cal.add("prodid", "-//Pommes de lune//pommesdelune.fr//") cal.add("version", "2.0") for event in events: From ce95fe6504e06839a4ca304fdbec32a34a8481e0 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 22:46:49 +0100 Subject: [PATCH 44/85] Ajout d'un sitemap Fix #246 --- src/agenda_culturel/settings/base.py | 2 -- src/agenda_culturel/sitemaps.py | 13 +++++++++++++ src/agenda_culturel/urls.py | 29 +++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/agenda_culturel/sitemaps.py diff --git a/src/agenda_culturel/settings/base.py b/src/agenda_culturel/settings/base.py index f00aef2..436d06f 100644 --- a/src/agenda_culturel/settings/base.py +++ b/src/agenda_culturel/settings/base.py @@ -61,8 +61,6 @@ INSTALLED_APPS = [ HONEYPOT_FIELD_NAME = "alias_name" -SITE_ID = 1 - MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", diff --git a/src/agenda_culturel/sitemaps.py b/src/agenda_culturel/sitemaps.py new file mode 100644 index 0000000..bc74502 --- /dev/null +++ b/src/agenda_culturel/sitemaps.py @@ -0,0 +1,13 @@ +from django.contrib import sitemaps +from django.urls import reverse + + +class StaticViewSitemap(sitemaps.Sitemap): + priority = 0.5 + changefreq = "daily" + + def items(self): + return ["home", "cette_semaine", "ce_mois_ci", "aujourdhui", "a_venir", "about", "contact"] + + def location(self, item): + return reverse(item) \ No newline at end of file diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index fd84eff..02fdff1 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -4,10 +4,31 @@ from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.urls import path, include, re_path from django.views.i18n import JavaScriptCatalog - +from django.contrib.sitemaps.views import sitemap +from django.contrib.sitemaps import GenericSitemap +from .sitemaps import StaticViewSitemap from .views import * +event_dict = { + "queryset": Event.objects.all(), + "date_field": "modified_date", +} +place_dict = { + "queryset": Place.objects.all(), +} +organisation_dict = { + "queryset": Organisation.objects.all(), +} + + +sitemaps = { + "static": StaticViewSitemap, + "events": GenericSitemap(event_dict, priority=0.7), + "places": GenericSitemap(place_dict, priority=0.6), + "organisations": GenericSitemap(organisation_dict, priority=0.2), +} + urlpatterns = [ path("", home, name="home"), path("semaine///", week_view, name="week_view"), @@ -178,6 +199,12 @@ urlpatterns = [ re_path(r'^robots\.txt', include('robots.urls')), path("__debug__/", include("debug_toolbar.urls")), path("ckeditor5/", include('django_ckeditor_5.urls')), + path( + "sitemap.xml", + sitemap, + {"sitemaps": sitemaps}, + name="django.contrib.sitemaps.views.sitemap", + ), ] if settings.DEBUG: From a1984f60f500ae20450d1265c12f49b83fe251b2 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 22:52:50 +0100 Subject: [PATCH 45/85] Ajout de cache sur le sitemap --- src/agenda_culturel/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index 02fdff1..1a8abfc 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -7,6 +7,7 @@ from django.views.i18n import JavaScriptCatalog from django.contrib.sitemaps.views import sitemap from django.contrib.sitemaps import GenericSitemap from .sitemaps import StaticViewSitemap +from django.views.decorators.cache import cache_page from .views import * @@ -201,7 +202,7 @@ urlpatterns = [ path("ckeditor5/", include('django_ckeditor_5.urls')), path( "sitemap.xml", - sitemap, + cache_page(86400)(sitemap), {"sitemaps": sitemaps}, name="django.contrib.sitemaps.views.sitemap", ), From 6e8f00ccbef6fd2b1a6633938214f26f419c7c04 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 8 Dec 2024 23:03:16 +0100 Subject: [PATCH 46/85] =?UTF-8?q?Am=C3=A9lioration=20url=20des=20organisat?= =?UTF-8?q?ions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/models.py | 2 +- src/agenda_culturel/urls.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index d405bd6..60bca41 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -545,7 +545,7 @@ class Organisation(models.Model): return self.name def get_absolute_url(self): - return reverse("view_organisation", kwargs={'pk': self.pk}) + return reverse("view_organisation", kwargs={'pk': self.pk, "extra": self.name}) diff --git a/src/agenda_culturel/urls.py b/src/agenda_culturel/urls.py index 1a8abfc..254b45b 100644 --- a/src/agenda_culturel/urls.py +++ b/src/agenda_culturel/urls.py @@ -156,7 +156,8 @@ urlpatterns = [ path("500/", internal_server_error, name="internal_server_error"), path("organisme//past", OrganisationDetailViewPast.as_view(), name="view_organisation_past"), - path("organisme/", OrganisationDetailView.as_view(), name="view_organisation"), + path("organisme/", OrganisationDetailView.as_view(), name="view_organisation_shortname"), + path("organisme/-", OrganisationDetailView.as_view(), name="view_organisation"), path("organisme/-/past", OrganisationDetailViewPast.as_view(), name="view_organisation_past_fullname"), path("organisme/-", OrganisationDetailView.as_view(), name="view_organisation_fullname"), path("organisme//edit", OrganisationUpdateView.as_view(), name="edit_organisation"), From c91cdf0c999bfb49285a033a23bfbef29e23af8a Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 9 Dec 2024 22:59:34 +0100 Subject: [PATCH 47/85] =?UTF-8?q?On=20ajoute=20la=20ligne=20now=20=C3=A0?= =?UTF-8?q?=20la=20fin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/calendar.py | 8 ++++++++ .../templates/agenda_culturel/page-week.html | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/agenda_culturel/calendar.py b/src/agenda_culturel/calendar.py index c80a922..2a5d73b 100644 --- a/src/agenda_culturel/calendar.py +++ b/src/agenda_culturel/calendar.py @@ -117,15 +117,23 @@ class DayInCalendar: if e.start_time is None else e.start_time ) + self.today_night = False if self.is_today(): + self.today_night = True now = timezone.now() nday = now.date() ntime = now.time() + found = False for idx,e in enumerate(self.events): if (nday < e.start_day) or (nday == e.start_day and e.start_time and ntime <= e.start_time): self.events[idx].is_first_after_now = True + found = True break + if found: + self.today_night = False + def is_today_after_events(self): + return self.is_today() and self.today_night def events_by_category_ordered(self): from .models import Category diff --git a/src/agenda_culturel/templates/agenda_culturel/page-week.html b/src/agenda_culturel/templates/agenda_culturel/page-week.html index 2f1db3a..9136e32 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-week.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-week.html @@ -150,6 +150,9 @@ {% endfor %} + {% if day.is_today_after_events %} +
  • {% now "H:i" %}
  • + {% endif %} {% endif %} From 3da9a5239ae47a912031916736746eb90637e681 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 9 Dec 2024 23:19:05 +0100 Subject: [PATCH 48/85] =?UTF-8?q?AJout=20de=20la=20ligne=20"now"=20sur=20l?= =?UTF-8?q?a=20vue=20"=C3=A0=20venir"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/static/style.scss | 4 ++++ .../templates/agenda_culturel/page-upcoming.html | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index 242bbca..824df90 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -1567,5 +1567,9 @@ label.required::after { } } margin-bottom: 0; + list-style: none; +} +.a-venir .line-now { + margin-left: -2em; } \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html b/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html index c1a1fcc..3ae0bf8 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html @@ -95,6 +95,9 @@

    {{ ti.short_name }} {{ ti.events|length }} {% picto_from_name "chevrons-down" %}

      {% for event in ti.events %} + {% if event.is_first_after_now %} +
    • {% now "H:i" %}
    • + {% endif %}
    • {{ event.category | circle_cat:event.has_recurrences }} {% if event.start_time %} {{ event.start_time }} @@ -102,6 +105,9 @@ {{ event|picto_status }} {{ event.title|no_emoji }} {{ event|tw_badge }}
    • {% endfor %} + {% if forloop.last and cd.is_today_after_events %} +
    • {% now "H:i" %}
    • + {% endif %}
    {% endif %} {% endfor %} From b1e541451986f988c37a4df4c4be9058372f6932 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 9 Dec 2024 23:20:13 +0100 Subject: [PATCH 49/85] =?UTF-8?q?renomme=20"=C3=A0=20venir"=20en=20"mainte?= =?UTF-8?q?nant"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/templates/agenda_culturel/page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/templates/agenda_culturel/page.html b/src/agenda_culturel/templates/agenda_culturel/page.html index 9dfff08..68f5296 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page.html +++ b/src/agenda_culturel/templates/agenda_culturel/page.html @@ -51,7 +51,7 @@ {% if user.is_authenticated %}{% block configurer-menu %}{% endblock %}{% endif %} {% block ajouter-menu %}{% endblock %} {% block rechercher-menu %}{% endblock %} -
  • À venir
  • +
  • Maintenant
  • Cette semaine
  • Ce mois-ci
  • From c1f7bfd8c4714aaa581652aff404257b01fd0a0a Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Wed, 11 Dec 2024 11:36:40 +0100 Subject: [PATCH 50/85] On renomme la classe ContactMessage en Message --- src/agenda_culturel/admin.py | 4 +- src/agenda_culturel/filters.py | 6 +-- src/agenda_culturel/forms.py | 6 +-- ...ctmessage_message_alter_message_options.py | 21 ++++++++ src/agenda_culturel/models.py | 12 ++--- ...elete.html => message_confirm_delete.html} | 0 ...ate_form.html => message_create_form.html} | 0 ...form.html => message_moderation_form.html} | 4 +- .../{contactmessages.html => messages.html} | 4 +- .../templates/agenda_culturel/page.html | 6 +-- .../templates/agenda_culturel/side-nav.html | 6 +-- .../single-event/event-single-inc.html | 8 +-- ...actmessages_extra.py => messages_extra.py} | 8 +-- src/agenda_culturel/urls.py | 20 +++---- src/agenda_culturel/views.py | 52 +++++++++---------- src/scripts/set_spam_all_contactmessages.py | 4 +- 16 files changed, 91 insertions(+), 70 deletions(-) create mode 100644 src/agenda_culturel/migrations/0125_rename_contactmessage_message_alter_message_options.py rename src/agenda_culturel/templates/agenda_culturel/{contactmessage_confirm_delete.html => message_confirm_delete.html} (100%) rename src/agenda_culturel/templates/agenda_culturel/{contactmessage_create_form.html => message_create_form.html} (100%) rename src/agenda_culturel/templates/agenda_culturel/{contactmessage_moderation_form.html => message_moderation_form.html} (88%) rename src/agenda_culturel/templates/agenda_culturel/{contactmessages.html => messages.html} (92%) rename src/agenda_culturel/templatetags/{contactmessages_extra.py => messages_extra.py} (77%) diff --git a/src/agenda_culturel/admin.py b/src/agenda_culturel/admin.py index 9de4a23..8c5d32a 100644 --- a/src/agenda_culturel/admin.py +++ b/src/agenda_culturel/admin.py @@ -9,7 +9,7 @@ from .models import ( BatchImportation, RecurrentImport, Place, - ContactMessage, + Message, ReferenceLocation, Organisation ) @@ -25,7 +25,7 @@ admin.site.register(DuplicatedEvents) admin.site.register(BatchImportation) admin.site.register(RecurrentImport) admin.site.register(Place) -admin.site.register(ContactMessage) +admin.site.register(Message) admin.site.register(ReferenceLocation) admin.site.register(Organisation) diff --git a/src/agenda_culturel/filters.py b/src/agenda_culturel/filters.py index a5d9d57..bb88335 100644 --- a/src/agenda_culturel/filters.py +++ b/src/agenda_culturel/filters.py @@ -44,7 +44,7 @@ from .models import ( Tag, Event, Category, - ContactMessage, + Message, DuplicatedEvents ) @@ -361,7 +361,7 @@ class EventFilterAdmin(django_filters.FilterSet): fields = ["status"] -class ContactMessagesFilterAdmin(django_filters.FilterSet): +class MessagesFilterAdmin(django_filters.FilterSet): closed = django_filters.MultipleChoiceFilter( label="Status", choices=((True, _("Closed")), (False, _("Open"))), @@ -374,7 +374,7 @@ class ContactMessagesFilterAdmin(django_filters.FilterSet): ) class Meta: - model = ContactMessage + model = Message fields = ["closed", "spam"] diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index fc272db..4570aaf 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -24,7 +24,7 @@ from .models import ( Place, Category, Tag, - ContactMessage + Message ) from django.conf import settings from django.core.files import File @@ -783,10 +783,10 @@ class PlaceForm(GroupFormMixin, ModelForm): def apply(self): return self.cleaned_data.get("apply_to_all") -class ContactMessageForm(ModelForm): +class MessageForm(ModelForm): class Meta: - model = ContactMessage + model = Message fields = ["subject", "name", "email", "message", "related_event"] widgets = {"related_event": HiddenInput()} diff --git a/src/agenda_culturel/migrations/0125_rename_contactmessage_message_alter_message_options.py b/src/agenda_culturel/migrations/0125_rename_contactmessage_message_alter_message_options.py new file mode 100644 index 0000000..958fb15 --- /dev/null +++ b/src/agenda_culturel/migrations/0125_rename_contactmessage_message_alter_message_options.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.9 on 2024-12-11 11:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0124_place_postcode'), + ] + + operations = [ + migrations.RenameModel( + old_name='ContactMessage', + new_name='Message', + ), + migrations.AlterModelOptions( + name='message', + options={'verbose_name': 'Message', 'verbose_name_plural': 'Messages'}, + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 60bca41..b404527 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1741,10 +1741,10 @@ class Event(models.Model): return [Event.get_count_modification(w) for w in when_list] -class ContactMessage(models.Model): +class Message(models.Model): class Meta: - verbose_name = _("Contact message") - verbose_name_plural = _("Contact messages") + verbose_name = _("Message") + verbose_name_plural = _("Messages") subject = models.CharField( verbose_name=_("Subject"), @@ -1800,11 +1800,11 @@ class ContactMessage(models.Model): null=True, ) - def nb_open_contactmessages(): - return ContactMessage.objects.filter(closed=False).count() + def nb_open_messages(): + return Message.objects.filter(closed=False).count() def get_absolute_url(self): - return reverse("contactmessage", kwargs={"pk": self.pk}) + return reverse("message", kwargs={"pk": self.pk}) class RecurrentImport(models.Model): diff --git a/src/agenda_culturel/templates/agenda_culturel/contactmessage_confirm_delete.html b/src/agenda_culturel/templates/agenda_culturel/message_confirm_delete.html similarity index 100% rename from src/agenda_culturel/templates/agenda_culturel/contactmessage_confirm_delete.html rename to src/agenda_culturel/templates/agenda_culturel/message_confirm_delete.html diff --git a/src/agenda_culturel/templates/agenda_culturel/contactmessage_create_form.html b/src/agenda_culturel/templates/agenda_culturel/message_create_form.html similarity index 100% rename from src/agenda_culturel/templates/agenda_culturel/contactmessage_create_form.html rename to src/agenda_culturel/templates/agenda_culturel/message_create_form.html diff --git a/src/agenda_culturel/templates/agenda_culturel/contactmessage_moderation_form.html b/src/agenda_culturel/templates/agenda_culturel/message_moderation_form.html similarity index 88% rename from src/agenda_culturel/templates/agenda_culturel/contactmessage_moderation_form.html rename to src/agenda_culturel/templates/agenda_culturel/message_moderation_form.html index 52bc8fe..6e2d656 100644 --- a/src/agenda_culturel/templates/agenda_culturel/contactmessage_moderation_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/message_moderation_form.html @@ -24,7 +24,7 @@
    -{% include "agenda_culturel/side-nav.html" with current="contactmessages" %} +{% include "agenda_culturel/side-nav.html" with current="messages" %}
    {% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/contactmessages.html b/src/agenda_culturel/templates/agenda_culturel/messages.html similarity index 92% rename from src/agenda_culturel/templates/agenda_culturel/contactmessages.html rename to src/agenda_culturel/templates/agenda_culturel/messages.html index b50e379..b9a587b 100644 --- a/src/agenda_culturel/templates/agenda_culturel/contactmessages.html +++ b/src/agenda_culturel/templates/agenda_culturel/messages.html @@ -45,7 +45,7 @@ {% for obj in paginator_filter %} {{ obj.date }} - {{ obj.subject }} + {{ obj.subject }} {{ obj.name }} {% if obj.related_event %}{{ obj.related_event.pk }}{% else %}/{% endif %} {% if obj.closed %}{% picto_from_name "check-square" "fermé" %}{% else %}{% picto_from_name "square" "ouvert" %}{% endif %} @@ -59,7 +59,7 @@
    -{% include "agenda_culturel/side-nav.html" with current="contactmessages" %} +{% include "agenda_culturel/side-nav.html" with current="messages" %} {% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/page.html b/src/agenda_culturel/templates/agenda_culturel/page.html index 68f5296..3016fc1 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page.html +++ b/src/agenda_culturel/templates/agenda_culturel/page.html @@ -2,7 +2,7 @@ {% load event_extra %} {% load cache %} - {% load contactmessages_extra %} + {% load messages_extra %} {% load utils_extra %} {% load duplicated_extra %} {% load rimports_extra %} @@ -77,8 +77,8 @@ {% 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" %} + {% if perms.agenda_culturel.view_message %} + {% show_badge_messages "bottom" %} {% endif %} {% if user.is_authenticated %} {{ user.username }} @ diff --git a/src/agenda_culturel/templates/agenda_culturel/side-nav.html b/src/agenda_culturel/templates/agenda_culturel/side-nav.html index dc8e4c6..c8b202d 100644 --- a/src/agenda_culturel/templates/agenda_culturel/side-nav.html +++ b/src/agenda_culturel/templates/agenda_culturel/side-nav.html @@ -1,5 +1,5 @@ {% load event_extra %} -{% load contactmessages_extra %} +{% load messages_extra %} {% load duplicated_extra %} {% load utils_extra %}