From ccf874bedca47156c5a70a705c8d4e8f738eee81 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sat, 26 Apr 2025 19:07:18 +0200 Subject: [PATCH 01/18] =?UTF-8?q?Am=C3=A9lioration=20des=20r=C3=A9sultats?= =?UTF-8?q?=20de=20recherche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agenda_culturel/filters.py b/src/agenda_culturel/filters.py index 8cab6da..023586d 100644 --- a/src/agenda_culturel/filters.py +++ b/src/agenda_culturel/filters.py @@ -555,8 +555,8 @@ class SimpleSearchEventFilter(django_filters.FilterSet): Func(Lower("description"), function="unaccent"), value ), ), - relevance=F("rank") + F("similarity"), - ).filter(Q(rank__gte=0.5) | Q(similarity__gte=0.3)) + relevance=0.7 * F("rank") + 0.3 * F("similarity"), + ).filter(Q(rank__gte=0.1) | Q(similarity__gte=0.3)) for f in [ "title", From c2cfe6cd02fbb9385767df3734d66f30a2c3330d Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 10:08:52 +0200 Subject: [PATCH 02/18] Update translations --- .../locale/fr/LC_MESSAGES/django.po | 286 +++++++++--------- 1 file changed, 143 insertions(+), 143 deletions(-) diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po index 096d18a..4b33216 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: 2025-04-26 11:22+0200\n" +"POT-Creation-Date: 2025-04-27 10:08+0200\n" "PO-Revision-Date: 2023-10-29 14:16+0000\n" "Last-Translator: Jean-Marie Favreau \n" "Language-Team: Jean-Marie Favreau \n" @@ -119,7 +119,7 @@ msgstr "" "directement. Les modérateurs peuvent choisir de le publier ou non." #: agenda_culturel/db_importer.py:376 agenda_culturel/db_importer.py:383 -#: agenda_culturel/models.py:2372 +#: agenda_culturel/models.py:2387 msgid "Warning" msgstr "Warning" @@ -201,11 +201,11 @@ msgid "Imported from" msgstr "Importé depuis" #: agenda_culturel/filters.py:478 agenda_culturel/models.py:872 -#: agenda_culturel/models.py:2671 +#: agenda_culturel/models.py:2686 msgid "Status" msgstr "Status" -#: agenda_culturel/filters.py:479 agenda_culturel/models.py:2434 +#: agenda_culturel/filters.py:479 agenda_culturel/models.py:2449 msgid "Closed" msgstr "Fermé" @@ -214,7 +214,7 @@ msgid "Open" msgstr "Ouvert" #: agenda_culturel/filters.py:483 agenda_culturel/filters.py:484 -#: agenda_culturel/models.py:2428 +#: agenda_culturel/models.py:2443 msgid "Spam" msgstr "Spam" @@ -222,7 +222,7 @@ msgstr "Spam" msgid "Non spam" msgstr "Non spam" -#: agenda_culturel/filters.py:489 agenda_culturel/models.py:2449 +#: agenda_culturel/filters.py:489 agenda_culturel/models.py:2464 msgid "Type" msgstr "Type" @@ -253,11 +253,11 @@ msgstr "" msgid "Your email" msgstr "Votre adresse email" -#: agenda_culturel/forms.py:168 agenda_culturel/models.py:2416 +#: agenda_culturel/forms.py:168 agenda_culturel/models.py:2431 msgid "Your email address" msgstr "Votre adresse email" -#: agenda_culturel/forms.py:174 agenda_culturel/models.py:2441 +#: agenda_culturel/forms.py:174 agenda_culturel/models.py:2456 msgid "Comments" msgstr "Commentaires" @@ -273,8 +273,8 @@ msgid "Receive notification of publication or leave a message for moderation" msgstr "Être notifié de la publication ou laisser un message à la modération" #: agenda_culturel/forms.py:212 agenda_culturel/models.py:312 -#: agenda_culturel/models.py:880 agenda_culturel/models.py:2595 -#: agenda_culturel/models.py:2706 +#: agenda_culturel/models.py:880 agenda_culturel/models.py:2610 +#: agenda_culturel/models.py:2721 msgid "Category" msgstr "Catégorie" @@ -329,7 +329,7 @@ msgid "Details" msgstr "Détails" #: agenda_culturel/forms.py:387 agenda_culturel/models.py:909 -#: agenda_culturel/models.py:2570 +#: agenda_culturel/models.py:2585 msgid "Location" msgstr "Localisation" @@ -409,7 +409,7 @@ msgid "Apply category {} to the event {}" msgstr "Appliquer la catégorie {} à l'événement {}" #: agenda_culturel/forms.py:872 agenda_culturel/models.py:697 -#: agenda_culturel/models.py:2758 +#: agenda_culturel/models.py:2773 msgid "Place" msgstr "Lieu" @@ -421,37 +421,37 @@ msgstr "Créer un lieu manquant" msgid "Add \"{}\" to the aliases of the place" msgstr "Ajouter « {} » aux alias du lieu" -#: agenda_culturel/forms.py:913 +#: agenda_culturel/forms.py:916 msgid "On saving, use aliases to detect all matching events with missing place" msgstr "" "Lors de l'enregistrement, utiliser des alias pour détecter tous les " "événements correspondants dont la place est manquante." -#: agenda_culturel/forms.py:926 +#: agenda_culturel/forms.py:929 msgid "Header" msgstr "Entête" -#: agenda_culturel/forms.py:929 agenda_culturel/models.py:659 +#: agenda_culturel/forms.py:932 agenda_culturel/models.py:659 msgid "Address" msgstr "Adresse" -#: agenda_culturel/forms.py:935 +#: agenda_culturel/forms.py:938 msgid "Meta" msgstr "Méta" -#: agenda_culturel/forms.py:938 +#: agenda_culturel/forms.py:941 msgid "Information" msgstr "Informations" -#: agenda_culturel/forms.py:990 +#: agenda_culturel/forms.py:993 msgid "Add a comment" msgstr "Ajouter un commentaire" -#: agenda_culturel/forms.py:1015 agenda_culturel/models.py:2859 +#: agenda_culturel/forms.py:1018 agenda_culturel/models.py:2874 msgid "Period type" msgstr "Type de période" -#: agenda_culturel/forms.py:1019 +#: agenda_culturel/forms.py:1022 msgid "ICAL file" msgstr "Fichier ICAL" @@ -563,7 +563,7 @@ msgstr "" #: agenda_culturel/models.py:195 agenda_culturel/models.py:242 #: agenda_culturel/models.py:321 agenda_culturel/models.py:616 #: agenda_culturel/models.py:657 agenda_culturel/models.py:763 -#: agenda_culturel/models.py:2408 agenda_culturel/models.py:2520 +#: agenda_culturel/models.py:2423 agenda_culturel/models.py:2535 msgid "Name" msgstr "Nom" @@ -632,8 +632,8 @@ msgstr "Description" msgid "Description of the tag" msgstr "Description de l'étiquette" -#: agenda_culturel/models.py:336 agenda_culturel/models.py:2375 -#: agenda_culturel/models.py:2422 +#: agenda_culturel/models.py:336 agenda_culturel/models.py:2390 +#: agenda_culturel/models.py:2437 msgid "Message" msgstr "Message" @@ -811,7 +811,7 @@ msgstr "Organisme" msgid "Organisations" msgstr "Organismes" -#: agenda_culturel/models.py:809 agenda_culturel/models.py:2565 +#: agenda_culturel/models.py:809 agenda_culturel/models.py:2580 msgid "Published" msgstr "Publié" @@ -847,7 +847,7 @@ msgstr "Auteur de la dernière modération" msgid "Title" msgstr "Titre" -#: agenda_culturel/models.py:886 agenda_culturel/models.py:2844 +#: agenda_culturel/models.py:886 agenda_culturel/models.py:2859 msgid "Start day" msgstr "Date de début" @@ -855,7 +855,7 @@ msgstr "Date de début" msgid "Start time" msgstr "Heure de début" -#: agenda_culturel/models.py:894 agenda_culturel/models.py:2845 +#: agenda_culturel/models.py:894 agenda_culturel/models.py:2860 msgid "End day" msgstr "Date de fin" @@ -965,199 +965,199 @@ msgstr "Ton événement a été publié" msgid "Your message has not been retained" msgstr "Ton événement n'a pas été retenu" -#: agenda_culturel/models.py:2363 +#: agenda_culturel/models.py:2378 msgid "From contributor" msgstr "D'un·e contributeurice" -#: agenda_culturel/models.py:2364 +#: agenda_culturel/models.py:2379 msgid "Import process" msgstr "Processus d'import" -#: agenda_culturel/models.py:2365 +#: agenda_culturel/models.py:2380 msgid "Update process" msgstr "Processus de mise à jour" -#: agenda_culturel/models.py:2366 +#: agenda_culturel/models.py:2381 msgid "Contact form" msgstr "Formulaire de contact" -#: agenda_culturel/models.py:2367 +#: agenda_culturel/models.py:2382 msgid "Event report" msgstr "Signalemet d'événement" -#: agenda_culturel/models.py:2370 +#: agenda_culturel/models.py:2385 msgid "From contributor (without message)" msgstr "D'un·e contributeurice (sans message)" -#: agenda_culturel/models.py:2376 +#: agenda_culturel/models.py:2391 msgid "Messages" msgstr "Messages" -#: agenda_culturel/models.py:2385 +#: agenda_culturel/models.py:2400 msgid "Subject" msgstr "Sujet" -#: agenda_culturel/models.py:2386 +#: agenda_culturel/models.py:2401 msgid "The subject of your message" msgstr "Sujet de votre message" -#: agenda_culturel/models.py:2392 +#: agenda_culturel/models.py:2407 msgid "Related event" msgstr "Événement associé" -#: agenda_culturel/models.py:2393 +#: agenda_culturel/models.py:2408 msgid "The message is associated with this event." msgstr "Le message est associé à cet événement." -#: agenda_culturel/models.py:2401 +#: agenda_culturel/models.py:2416 msgid "Author of the message" msgstr "Auteur du message" -#: agenda_culturel/models.py:2409 +#: agenda_culturel/models.py:2424 msgid "Your name" msgstr "Votre nom" -#: agenda_culturel/models.py:2415 +#: agenda_culturel/models.py:2430 msgid "Email address" msgstr "Adresse email" -#: agenda_culturel/models.py:2422 +#: agenda_culturel/models.py:2437 msgid "Your message" msgstr "Votre message" -#: agenda_culturel/models.py:2429 +#: agenda_culturel/models.py:2444 msgid "This message is a spam." msgstr "Ce message est un spam." -#: agenda_culturel/models.py:2436 +#: agenda_culturel/models.py:2451 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:2442 +#: agenda_culturel/models.py:2457 msgid "Comments on the message from the moderation team" msgstr "Commentaires sur ce message par l'équipe de modération" -#: agenda_culturel/models.py:2475 agenda_culturel/models.py:2653 +#: agenda_culturel/models.py:2490 agenda_culturel/models.py:2668 msgid "Recurrent import" msgstr "Import récurrent" -#: agenda_culturel/models.py:2476 +#: agenda_culturel/models.py:2491 msgid "Recurrent imports" msgstr "Imports récurrents" -#: agenda_culturel/models.py:2480 +#: agenda_culturel/models.py:2495 msgid "ical" msgstr "ical" -#: agenda_culturel/models.py:2481 +#: agenda_culturel/models.py:2496 msgid "ical no busy" msgstr "ical sans busy" -#: agenda_culturel/models.py:2482 +#: agenda_culturel/models.py:2497 msgid "ical no VC" msgstr "ical sans VC" -#: agenda_culturel/models.py:2483 +#: agenda_culturel/models.py:2498 msgid "ical naive timezone" msgstr "ical timezone naïve" -#: agenda_culturel/models.py:2484 +#: agenda_culturel/models.py:2499 msgid "lacoope.org" msgstr "lacoope.org" -#: agenda_culturel/models.py:2485 +#: agenda_culturel/models.py:2500 msgid "la comédie" msgstr "la comédie" -#: agenda_culturel/models.py:2486 +#: agenda_culturel/models.py:2501 msgid "le fotomat" msgstr "le fotomat" -#: agenda_culturel/models.py:2487 +#: agenda_culturel/models.py:2502 msgid "la puce à l'oreille" msgstr "la puce à loreille" -#: agenda_culturel/models.py:2488 +#: agenda_culturel/models.py:2503 msgid "Plugin wordpress MEC" msgstr "Plugin wordpress MEC" -#: agenda_culturel/models.py:2489 +#: agenda_culturel/models.py:2504 msgid "Événements d'une page FB" msgstr "Événements d'une page FB" -#: agenda_culturel/models.py:2490 +#: agenda_culturel/models.py:2505 msgid "Billetterie Clermont-Ferrand" msgstr "" -#: agenda_culturel/models.py:2491 +#: agenda_culturel/models.py:2506 msgid "Arachnée concert" msgstr "Arachnée concert" -#: agenda_culturel/models.py:2492 +#: agenda_culturel/models.py:2507 msgid "Le Rio" msgstr "Le Rio" -#: agenda_culturel/models.py:2493 +#: agenda_culturel/models.py:2508 msgid "La Raymonde" msgstr "La Raymone" -#: agenda_culturel/models.py:2494 +#: agenda_culturel/models.py:2509 msgid "Agenda apidae tourisme" msgstr "Agenda apidae tourisme" -#: agenda_culturel/models.py:2495 +#: agenda_culturel/models.py:2510 msgid "Agenda iguana (médiathèques)" msgstr "Agenda iguana (médiathèques)" -#: agenda_culturel/models.py:2496 +#: agenda_culturel/models.py:2511 msgid "Mille formes" msgstr "Mille Formes" -#: agenda_culturel/models.py:2497 +#: agenda_culturel/models.py:2512 msgid "Les Amis du Temps des Cerises" msgstr "Les Amis du Temps des Cerises" -#: agenda_culturel/models.py:2498 +#: agenda_culturel/models.py:2513 msgid "Mobilizon" msgstr "Mobilizon" -#: agenda_culturel/models.py:2499 +#: agenda_culturel/models.py:2514 msgid "Le caméléon" msgstr "" -#: agenda_culturel/models.py:2500 +#: agenda_culturel/models.py:2515 msgid "Echosciences" msgstr "" -#: agenda_culturel/models.py:2501 +#: agenda_culturel/models.py:2516 msgid "Hello Asso" msgstr "Hello Asso" -#: agenda_culturel/models.py:2504 +#: agenda_culturel/models.py:2519 msgid "simple" msgstr "simple" -#: agenda_culturel/models.py:2505 +#: agenda_culturel/models.py:2520 msgid "Headless Chromium" msgstr "chromium sans interface" -#: agenda_culturel/models.py:2508 +#: agenda_culturel/models.py:2523 msgid "Headless Chromium (pause)" msgstr "chromium sans interface (pause)" -#: agenda_culturel/models.py:2514 +#: agenda_culturel/models.py:2529 msgid "daily" msgstr "chaque jour" -#: agenda_culturel/models.py:2516 +#: agenda_culturel/models.py:2531 msgid "weekly" msgstr "chaque semaine" -#: agenda_culturel/models.py:2517 +#: agenda_culturel/models.py:2532 msgid "never" msgstr "jamais" -#: agenda_culturel/models.py:2522 +#: agenda_culturel/models.py:2537 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." @@ -1165,151 +1165,151 @@ 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:2529 +#: agenda_culturel/models.py:2544 msgid "Processor" msgstr "Processeur" -#: agenda_culturel/models.py:2535 +#: agenda_culturel/models.py:2550 msgid "Downloader" msgstr "Téléchargeur" -#: agenda_culturel/models.py:2542 +#: agenda_culturel/models.py:2557 msgid "Import recurrence" msgstr "Récurrence d'import" -#: agenda_culturel/models.py:2549 +#: agenda_culturel/models.py:2564 msgid "Source" msgstr "Source" -#: agenda_culturel/models.py:2550 +#: agenda_culturel/models.py:2565 msgid "URL of the source document" msgstr "URL du document source" -#: agenda_culturel/models.py:2555 +#: agenda_culturel/models.py:2570 msgid "Browsable url" msgstr "URL navigable" -#: agenda_culturel/models.py:2557 +#: agenda_culturel/models.py:2572 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:2566 +#: agenda_culturel/models.py:2581 msgid "Status of each imported event (published or draft)" msgstr "Status de chaque événement importé (publié ou brouillon)" -#: agenda_culturel/models.py:2571 +#: agenda_culturel/models.py:2586 msgid "Address for each imported event" msgstr "Adresse de chaque événement importé" -#: agenda_culturel/models.py:2578 +#: agenda_culturel/models.py:2593 msgid "Force location" msgstr "Focer la localisation" -#: agenda_culturel/models.py:2579 +#: agenda_culturel/models.py:2594 msgid "force location even if another is detected." msgstr "Forcer la localisation même si une autre a été détectée." -#: agenda_culturel/models.py:2585 +#: agenda_culturel/models.py:2600 msgid "Organiser" msgstr "Organisation" -#: agenda_culturel/models.py:2586 +#: agenda_culturel/models.py:2601 msgid "Organiser of each imported event" msgstr "Organisme à l'origine de chaque événement importé" -#: agenda_culturel/models.py:2596 +#: agenda_culturel/models.py:2611 msgid "Category of each imported event" msgstr "Catégorie de chaque événement importé" -#: agenda_culturel/models.py:2604 +#: agenda_culturel/models.py:2619 msgid "Tags for each imported event" msgstr "Étiquettes de chaque événement importé" -#: agenda_culturel/models.py:2605 +#: agenda_culturel/models.py:2620 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:2634 +#: agenda_culturel/models.py:2649 msgid "Running" msgstr "En cours" -#: agenda_culturel/models.py:2635 +#: agenda_culturel/models.py:2650 msgid "Canceled" msgstr "Annulé" -#: agenda_culturel/models.py:2636 +#: agenda_culturel/models.py:2651 msgid "Success" msgstr "Succès" -#: agenda_culturel/models.py:2637 +#: agenda_culturel/models.py:2652 msgid "Failed" msgstr "Erreur" -#: agenda_culturel/models.py:2640 +#: agenda_culturel/models.py:2655 msgid "Batch importation" msgstr "Importation par lot" -#: agenda_culturel/models.py:2641 +#: agenda_culturel/models.py:2656 msgid "Batch importations" msgstr "Importations par lot" -#: agenda_culturel/models.py:2654 +#: agenda_culturel/models.py:2669 msgid "Reference to the recurrent import processing" msgstr "Référence du processus d'import récurrent" -#: agenda_culturel/models.py:2662 +#: agenda_culturel/models.py:2677 msgid "URL (if not recurrent import)" msgstr "URL (si pas d'import récurrent)" -#: agenda_culturel/models.py:2663 +#: agenda_culturel/models.py:2678 msgid "Source URL if no RecurrentImport is associated." msgstr "URL source si aucun import récurrent n'est associé" -#: agenda_culturel/models.py:2678 +#: agenda_culturel/models.py:2693 msgid "Error message" msgstr "Votre message" -#: agenda_culturel/models.py:2682 +#: agenda_culturel/models.py:2697 msgid "Number of collected events" msgstr "Nombre d'événements collectés" -#: agenda_culturel/models.py:2685 +#: agenda_culturel/models.py:2700 msgid "Number of imported events" msgstr "Nombre d'événements importés" -#: agenda_culturel/models.py:2688 +#: agenda_culturel/models.py:2703 msgid "Number of updated events" msgstr "Nombre d'événements mis à jour" -#: agenda_culturel/models.py:2691 +#: agenda_culturel/models.py:2706 msgid "Number of removed events" msgstr "Nombre d'événements supprimés" -#: agenda_culturel/models.py:2699 +#: agenda_culturel/models.py:2714 msgid "Weight" msgstr "Poids" -#: agenda_culturel/models.py:2700 +#: agenda_culturel/models.py:2715 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:2707 +#: agenda_culturel/models.py:2722 msgid "Category applied to the event" msgstr "Catégorie appliquée à l'événement" -#: agenda_culturel/models.py:2712 +#: agenda_culturel/models.py:2727 msgid "Contained in the title" msgstr "Contenu dans le titre" -#: agenda_culturel/models.py:2713 +#: agenda_culturel/models.py:2728 msgid "Text contained in the event title" msgstr "Texte contenu dans le titre de l'événement" -#: agenda_culturel/models.py:2719 +#: agenda_culturel/models.py:2734 msgid "Exact title extract" msgstr "Extrait exact du titre" -#: agenda_culturel/models.py:2721 +#: agenda_culturel/models.py:2736 msgid "" "If checked, the extract will be searched for in the title using the exact " "form (capitals, accents)." @@ -1317,19 +1317,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans le titre en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2727 +#: agenda_culturel/models.py:2742 msgid "Contained in the description" msgstr "Contenu dans la description" -#: agenda_culturel/models.py:2728 +#: agenda_culturel/models.py:2743 msgid "Text contained in the description" msgstr "Texte contenu dans la description" -#: agenda_culturel/models.py:2734 +#: agenda_culturel/models.py:2749 msgid "Exact description extract" msgstr "Extrait exact de description" -#: agenda_culturel/models.py:2736 +#: agenda_culturel/models.py:2751 msgid "" "If checked, the extract will be searched for in the description using the " "exact form (capitals, accents)." @@ -1337,19 +1337,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans la description en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2742 +#: agenda_culturel/models.py:2757 msgid "Contained in the location" msgstr "Contenu dans la localisation" -#: agenda_culturel/models.py:2743 +#: agenda_culturel/models.py:2758 msgid "Text contained in the event location" msgstr "Texte contenu dans la localisation de l'événement" -#: agenda_culturel/models.py:2749 +#: agenda_culturel/models.py:2764 msgid "Exact location extract" msgstr "Extrait exact de localisation" -#: agenda_culturel/models.py:2751 +#: agenda_culturel/models.py:2766 msgid "" "If checked, the extract will be searched for in the location using the exact " "form (capitals, accents)." @@ -1357,51 +1357,51 @@ msgstr "" "Si coché, l'extrait sera recherché dans la localisation en utilisant la " "forme exacte (majuscules, accents)" -#: agenda_culturel/models.py:2759 +#: agenda_culturel/models.py:2774 msgid "Location from place" msgstr "Localisation depuis le lieu" -#: agenda_culturel/models.py:2768 +#: agenda_culturel/models.py:2783 msgid "Categorisation rule" msgstr "Règle de catégorisation" -#: agenda_culturel/models.py:2769 +#: agenda_culturel/models.py:2784 msgid "Categorisation rules" msgstr "Règles de catégorisation" -#: agenda_culturel/models.py:2843 +#: agenda_culturel/models.py:2858 msgid "Period name" msgstr "Nom de la période" -#: agenda_culturel/models.py:2848 +#: agenda_culturel/models.py:2863 msgid "Special period" msgstr "Période remarquable" -#: agenda_culturel/models.py:2849 +#: agenda_culturel/models.py:2864 msgid "Special periods" msgstr "Périodes remarquables" -#: agenda_culturel/models.py:2855 +#: agenda_culturel/models.py:2870 msgid "public holidays" msgstr "Jour férié" -#: agenda_culturel/models.py:2856 +#: agenda_culturel/models.py:2871 msgid "school vacations" msgstr "Vacances scolaires" -#: agenda_culturel/models.py:2874 +#: agenda_culturel/models.py:2889 msgid "The end date must be after or equal to the start date." msgstr "La date de fin doit être après ou identique à la date de début." -#: agenda_culturel/models.py:2882 +#: agenda_culturel/models.py:2897 msgid " on " msgstr " du " -#: agenda_culturel/models.py:2885 +#: agenda_culturel/models.py:2900 msgid " from " msgstr " du " -#: agenda_culturel/models.py:2885 +#: agenda_culturel/models.py:2900 msgid " to " msgstr " au " @@ -1459,21 +1459,21 @@ msgstr ": erreur 500" msgid "An internal error has occurred on site {} at address {}." msgstr "Une erreur interne s'est produite sur le site {} à l'adresse {}." -#: agenda_culturel/views/event_duplicate_views.py:72 +#: agenda_culturel/views/event_duplicate_views.py:76 msgid "Update successfully completed." msgstr "Mise à jour réalisée avec succès." -#: agenda_culturel/views/event_duplicate_views.py:140 +#: agenda_culturel/views/event_duplicate_views.py:150 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/event_duplicate_views.py:177 +#: agenda_culturel/views/event_duplicate_views.py:187 msgid "Events have been marked as unduplicated." msgstr "Les événements ont été marqués comme non dupliqués." -#: agenda_culturel/views/event_duplicate_views.py:194 -#: agenda_culturel/views/event_duplicate_views.py:211 -#: agenda_culturel/views/event_duplicate_views.py:240 +#: agenda_culturel/views/event_duplicate_views.py:204 +#: agenda_culturel/views/event_duplicate_views.py:221 +#: agenda_culturel/views/event_duplicate_views.py:250 msgid "" "The selected item is no longer included in the list of duplicates. Someone " "else has probably modified the list in the meantime." @@ -1481,23 +1481,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/event_duplicate_views.py:201 +#: agenda_culturel/views/event_duplicate_views.py:211 msgid "The selected event has been set as representative" msgstr "L'événement sélectionné a été défini comme representatif." -#: agenda_culturel/views/event_duplicate_views.py:226 +#: agenda_culturel/views/event_duplicate_views.py:236 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/event_duplicate_views.py:278 +#: agenda_culturel/views/event_duplicate_views.py:288 msgid "Cleaning up duplicates: {} item(s) fixed." msgstr "Nettoyage des dupliqués: {} élément(s) corrigé(s)." -#: agenda_culturel/views/event_duplicate_views.py:327 +#: agenda_culturel/views/event_duplicate_views.py:337 msgid "The event was successfully duplicated." msgstr "L'événement a été marqué dupliqué avec succès." -#: agenda_culturel/views/event_duplicate_views.py:335 +#: agenda_culturel/views/event_duplicate_views.py:345 msgid "" "The event has been successfully flagged as a duplicate. The moderation team " "will deal with your suggestion shortly." From 31a008478423c376b8581e84b231b9314cdd8aa1 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 10:19:30 +0200 Subject: [PATCH 03/18] on supprime le code postal qui ne facilite pas la recherche d'adresses --- .../templates/agenda_culturel/event_set_place_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agenda_culturel/templates/agenda_culturel/event_set_place_form.html b/src/agenda_culturel/templates/agenda_culturel/event_set_place_form.html index 191d172..5e21b76 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event_set_place_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/event_set_place_form.html @@ -76,7 +76,7 @@ choices.showDropdown(); setTimeout(() => { - const searchTerm = htmlDecode('{{ object.location }}'); + const searchTerm = htmlDecode('{{ object.location }}').replace(/\(?\d{5}\)?/, ''); choices.input.focus(); choices.input.value = searchTerm; choices._handleSearch(searchTerm); From e54ef63b01549eff37dea5ebf4062a87283d3a16 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 10:48:03 +0200 Subject: [PATCH 04/18] On affiche tous les messages dans le diff --- src/agenda_culturel/models.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index fee31d1..75df6f7 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -595,12 +595,7 @@ class DuplicatedEvents(models.Model): def get_import_messages(self): msgs = [] for e in self.get_duplicated(): - for m in e.message_set.filter( - message_type__in=[ - Message.TYPE.IMPORT_PROCESS, - Message.TYPE.UPDATE_PROCESS, - ] - ).order_by("date"): + for m in e.message_set.order_by("date"): msgs.append(m) return msgs @@ -1014,12 +1009,7 @@ class Event(models.Model): self._messages = [] def get_import_messages(self): - return self.message_set.filter( - message_type__in=[ - Message.TYPE.IMPORT_PROCESS, - Message.TYPE.UPDATE_PROCESS, - ] - ).order_by("date") + return self.message_set.order_by("date") def get_consolidated_end_day(self, intuitive=True): if intuitive: From ceaf13085a2c9b88b1d76043912393fb25699218 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 11:17:19 +0200 Subject: [PATCH 05/18] =?UTF-8?q?Am=C3=A9lioration=20recherche=20=C3=A9v?= =?UTF-8?q?=C3=A9nements=20similaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #400 --- src/agenda_culturel/models.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 75df6f7..5dfad68 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1187,8 +1187,28 @@ class Event(models.Model): output_field=models.IntegerField(), ) ) + with_emoji = [ + s for s in self.tags if any(char in emoji.EMOJI_DATA for char in s) + ] + if len(with_emoji) > 0: + qs = qs.annotate( + overlap_emoji_tags=RawSQL( + sql="ARRAY(select UNNEST(%s::text[]) INTERSECT select UNNEST(tags))", + params=(with_emoji,), + output_field=ArrayField(models.CharField(max_length=50)), + ) + ).annotate( + overlap_emoji_tags_count=Func( + F("overlap_emoji_tags"), + function="CARDINALITY", + output_field=models.IntegerField(), + ) + ) + else: + qs = qs.annotate(overlap_emoji_tags_count=Value(0)) + else: - qs = qs.annotate(overlap_tags_count=Value(1)) + qs = qs.annotate(overlap_tags_count=Value(0)) if self.exact_location: qs = ( @@ -1216,7 +1236,8 @@ class Event(models.Model): ) ) .annotate( - score=F("overlap_tags_count") * 30 + score=F("overlap_tags_count") * 20 + + F("overlap_emoji_tags_count") * 40 + F("similarity_title") * 2 + F("similarity_description") * 10 + 10 / (F("distance") + 1) From bd97bc0976e372603d2c997a5349840c3c67b99a Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 12:14:19 +0200 Subject: [PATCH 06/18] =?UTF-8?q?Am=C3=A9lioration=20soumission=20d'=C3=A9?= =?UTF-8?q?v=C3=A9nement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - message succès plutôt que information - ajout d'un clean_url par défaut --- src/agenda_culturel/import_tasks/extractor.py | 6 +++--- src/agenda_culturel/views/event_views.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/agenda_culturel/import_tasks/extractor.py b/src/agenda_culturel/import_tasks/extractor.py index 2d60ba6..7a0a9ab 100644 --- a/src/agenda_culturel/import_tasks/extractor.py +++ b/src/agenda_culturel/import_tasks/extractor.py @@ -190,6 +190,9 @@ class Extractor(ABC): def is_known_url(url): return False + def clean_url(url): + return url + def set_header(self, url): self.header["url"] = url self.header["date"] = datetime.now() @@ -372,6 +375,3 @@ class EventNotFoundExtractor(Extractor): ) return self.get_structure() - - def clean_url(url): - return url diff --git a/src/agenda_culturel/views/event_views.py b/src/agenda_culturel/views/event_views.py index 45368ff..3547409 100644 --- a/src/agenda_culturel/views/event_views.py +++ b/src/agenda_culturel/views/event_views.py @@ -340,7 +340,7 @@ def import_event_proxy(request): ) return HttpResponseRedirect(ex.get_absolute_url()) else: - messages.info( + messages.success( request, _( "This type of address is known to the calendar, so an automatic import is proposed." From 53ce6ad29fdd2b0c4ecd5b5c8143a0193c0cab39 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 12:15:03 +0200 Subject: [PATCH 07/18] =?UTF-8?q?Correction=20de=20l'annotation=20des=20?= =?UTF-8?q?=C3=A9v=C3=A9nements=20sugg=C3=A9r=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 5dfad68..436d26e 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -1208,7 +1208,9 @@ class Event(models.Model): qs = qs.annotate(overlap_emoji_tags_count=Value(0)) else: - qs = qs.annotate(overlap_tags_count=Value(0)) + qs = qs.annotate( + overlap_tags_count=Value(0), overlap_emoji_tags_count=Value(0) + ) if self.exact_location: qs = ( From e38d31edc85cbd7095ed9fba1531ebcc2834db71 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 12:15:15 +0200 Subject: [PATCH 08/18] =?UTF-8?q?importation=20d'=C3=A9v=C3=A9nement=20mob?= =?UTF-8?q?ilizon=20unique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #394 --- experimentations/get_mobilizon.py | 8 +- src/agenda_culturel/import_tasks/extractor.py | 2 + .../generic_extractors/mobilizon.py | 196 ++++++++++-------- 3 files changed, 120 insertions(+), 86 deletions(-) diff --git a/experimentations/get_mobilizon.py b/experimentations/get_mobilizon.py index 61ba905..adc4644 100755 --- a/experimentations/get_mobilizon.py +++ b/experimentations/get_mobilizon.py @@ -24,19 +24,19 @@ from src.agenda_culturel.import_tasks.importer import URL2Events if __name__ == "__main__": u2e = URL2Events(SimpleDownloader(), mobilizon.CExtractor()) - url = "https://mobilizon.fr/@attac63/events?" - url_human = "https://mobilizon.fr/@attac63/events" + url = "https://keskonfai.fr/events/166fca9c-e758-437c-8002-9a55d822e34d" + url_human = "https://keskonfai.fr/events/166fca9c-e758-437c-8002-9a55d822e34d" try: events = u2e.process( url, url_human, - cache="cache-attac63.html", + cache="cache-single-event-mobilizon.html", default_values={}, published=True, ) - exportfile = "events-attac63.json" + exportfile = "events-single-event-mobilizon.json" print("Saving events to file {}".format(exportfile)) with open(exportfile, "w") as f: json.dump(events, f, indent=4, default=str) diff --git a/src/agenda_culturel/import_tasks/extractor.py b/src/agenda_culturel/import_tasks/extractor.py index 7a0a9ab..9729a61 100644 --- a/src/agenda_culturel/import_tasks/extractor.py +++ b/src/agenda_culturel/import_tasks/extractor.py @@ -315,6 +315,7 @@ class Extractor(ABC): from .generic_extractors.ical import ICALExtractor from .custom_extractors.associations_cf import CExtractor as AssociationsCF from .generic_extractors.helloasso import CExtractor as HelloAssoExtractor + from .generic_extractors.mobilizon import CExtractor as MobilizonExtractor if single_event: return [ @@ -323,6 +324,7 @@ class Extractor(ABC): AssociationsCF, ICALExtractor, HelloAssoExtractor, + MobilizonExtractor, EventNotFoundExtractor, ] else: diff --git a/src/agenda_culturel/import_tasks/generic_extractors/mobilizon.py b/src/agenda_culturel/import_tasks/generic_extractors/mobilizon.py index 0ff2511..c34c2ea 100644 --- a/src/agenda_culturel/import_tasks/generic_extractors/mobilizon.py +++ b/src/agenda_culturel/import_tasks/generic_extractors/mobilizon.py @@ -13,10 +13,50 @@ logger = logging.getLogger(__name__) # A class dedicated to get events from Mobilizon class CExtractor(Extractor): + event_params = """ + id, + title, + url, + beginsOn, + endsOn, + options { + showStartTime, + showEndTime, + timezone + }, + attributedTo { + avatar { + url, + } + name, + preferredUsername, + }, + description, + onlineAddress, + physicalAddress { + locality, + description, + region + }, + tags { + title, + id, + slug + }, + picture { + url + }, + status + """ + def __init__(self): super().__init__() self.no_downloader = True + def is_known_url(url, include_links=True): + u = urlparse(url) + return u.netloc in ["keskonfai.fr", "mobilizon.fr"] + # Source code adapted from https://framagit.org/Marc-AntoineA/mobilizon-client-python def _request(self, body, data): headers = {} @@ -57,49 +97,20 @@ query($preferredUsername: String!, $afterDatetime: DateTime) { def _oncoming_events(self): def _oncoming_events_page(page): - query = """ + query = ( + """ query($preferredUsername: String!, $afterDatetime: DateTime, $page: Int) { group(preferredUsername: $preferredUsername) { organizedEvents(afterDatetime: $afterDatetime, page: $page) { - elements { - id, - title, - url, - beginsOn, - endsOn, - options { - showStartTime, - showEndTime, - timezone - }, - attributedTo { - avatar { - url, - } - name, - preferredUsername, - }, - description, - onlineAddress, - physicalAddress { - locality, - description, - region - }, - tags { - title, - id, - slug - }, - picture { - url - }, - status + elements {""" + + CExtractor.event_params + + """ } } } } """ + ) today = datetime.now(timezone.utc).isoformat() data = { @@ -119,6 +130,68 @@ query($preferredUsername: String!, $afterDatetime: DateTime, $page: Int) { page += 1 return events + def _get_event(self): + query = ( + "query GetEvent($uuid: UUID!) { event(uuid: $uuid) {" + + CExtractor.event_params + + "}}" + ) + data = { + "uuid": self._uuid_event, + } + + r = self._request(query, data) + return r["event"] + + def add_mobilizon_event(self, e, default_values, published): + title = e["title"] + event_url = e["url"] + if "picture" in e and e["picture"] is not None: + image = e["picture"]["url"] + else: + image = None + location = ( + e["physicalAddress"]["description"] + + ", " + + e["physicalAddress"]["locality"] + ) + soup = BeautifulSoup(e["description"], "html.parser") + + description = soup.get_text(separator="\n") + start = ( + dateutil.parser.isoparse(e["beginsOn"]) + .replace(tzinfo=timezone.utc) + .astimezone(tz=None) + ) + end = ( + dateutil.parser.isoparse(e["endsOn"]) + .replace(tzinfo=timezone.utc) + .astimezone(tz=None) + ) + + start_day = start.date() + start_time = start.time() if e["options"]["showStartTime"] else None + end_day = end.date() + end_time = end.time() if e["options"]["showEndTime"] else None + + self.add_event( + default_values, + title, + None, + start_day, + location, + description, + [], + uuids=[event_url], + recurrences=None, + url_human=event_url, + start_time=start_time, + published=published, + image=image, + end_day=end_day, + end_time=end_time, + ) + def extract( self, content, @@ -145,52 +218,11 @@ query($preferredUsername: String!, $afterDatetime: DateTime, $page: Int) { events = self._oncoming_events() for e in events: - title = e["title"] - event_url = e["url"] - if "picture" in e and e["picture"] is not None: - image = e["picture"]["url"] - else: - image = None - location = ( - e["physicalAddress"]["description"] - + ", " - + e["physicalAddress"]["locality"] - ) - soup = BeautifulSoup(e["description"], "html.parser") - - description = soup.get_text(separator="\n") - start = ( - dateutil.parser.isoparse(e["beginsOn"]) - .replace(tzinfo=timezone.utc) - .astimezone(tz=None) - ) - end = ( - dateutil.parser.isoparse(e["endsOn"]) - .replace(tzinfo=timezone.utc) - .astimezone(tz=None) - ) - - start_day = start.date() - start_time = start.time() if e["options"]["showStartTime"] else None - end_day = end.date() - end_time = end.time() if e["options"]["showEndTime"] else None - - self.add_event( - default_values, - title, - None, - start_day, - location, - description, - [], - uuids=[event_url], - recurrences=None, - url_human=event_url, - start_time=start_time, - published=published, - image=image, - end_day=end_day, - end_time=end_time, - ) + self.add_mobilizon_event(e, default_values, published) + elif "events" in url: + self._api_end_point = "https://" + urlparse(url).netloc + "/api" + self._uuid_event = url.split("/")[-1] + event = self._get_event() + self.add_mobilizon_event(event, default_values, published) return self.get_structure() From 8f488cf7c53a31dd0d67d3e1c47b257c3778a2b9 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 23:12:10 +0200 Subject: [PATCH 09/18] =?UTF-8?q?On=20permet=20l'import=20des=20liens=20pr?= =?UTF-8?q?=C3=A9sents=20dans=20une=20page=20html=20coll=C3=A9e=20dans=20l?= =?UTF-8?q?e=20formulaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agenda_culturel/celery.py | 19 +- src/agenda_culturel/forms.py | 6 +- .../locale/fr/LC_MESSAGES/django.po | 410 +++++++++--------- .../batchimportation_form.html | 34 +- .../templates/agenda_culturel/side-nav.html | 4 + .../views/import_batch_views.py | 37 +- 6 files changed, 289 insertions(+), 221 deletions(-) diff --git a/src/agenda_culturel/celery.py b/src/agenda_culturel/celery.py index 4478e57..77965a7 100644 --- a/src/agenda_culturel/celery.py +++ b/src/agenda_culturel/celery.py @@ -540,13 +540,20 @@ def import_events_from_urls( ): for ucat in urls_cat_tags: if ucat is not None: - url = ucat[0] - cat = ucat[1] - tags = ucat[2] + url = None + cat = None + tags = None + if isinstance(ucat, str): + url = ucat + elif isinstance(ucat, (list, tuple)): + url = ucat[0] + cat = ucat[1] + tags = ucat[2] - import_events_from_url.delay( - url, cat, tags, user_id=user_id, email=email, comments=comments - ) + if url is not None: + import_events_from_url.delay( + url, cat, tags, user_id=user_id, email=email, comments=comments + ) @app.task(base=ChromiumTask, bind=True) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index 9c25b46..c8f3f60 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -537,10 +537,10 @@ class EventModerateForm(ModelForm): class BatchImportationForm(Form): required_css_class = "required" - json = CharField( - label="JSON", + data = CharField( + label=_("Data"), widget=Textarea(attrs={"rows": "10"}), - help_text=_("JSON in the format expected for the import."), + help_text=_("Supported formats: json, html."), required=True, ) diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po index 4b33216..dafeb2e 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: 2025-04-27 10:08+0200\n" +"POT-Creation-Date: 2025-04-27 23:08+0200\n" "PO-Revision-Date: 2023-10-29 14:16+0000\n" "Last-Translator: Jean-Marie Favreau \n" "Language-Team: Jean-Marie Favreau \n" @@ -119,7 +119,7 @@ msgstr "" "directement. Les modérateurs peuvent choisir de le publier ou non." #: agenda_culturel/db_importer.py:376 agenda_culturel/db_importer.py:383 -#: agenda_culturel/models.py:2387 +#: agenda_culturel/models.py:2400 msgid "Warning" msgstr "Warning" @@ -200,12 +200,12 @@ msgstr "dernier créé d'abord" msgid "Imported from" msgstr "Importé depuis" -#: agenda_culturel/filters.py:478 agenda_culturel/models.py:872 -#: agenda_culturel/models.py:2686 +#: agenda_culturel/filters.py:478 agenda_culturel/models.py:867 +#: agenda_culturel/models.py:2699 msgid "Status" msgstr "Status" -#: agenda_culturel/filters.py:479 agenda_culturel/models.py:2449 +#: agenda_culturel/filters.py:479 agenda_culturel/models.py:2462 msgid "Closed" msgstr "Fermé" @@ -214,7 +214,7 @@ msgid "Open" msgstr "Ouvert" #: agenda_culturel/filters.py:483 agenda_culturel/filters.py:484 -#: agenda_culturel/models.py:2443 +#: agenda_culturel/models.py:2456 msgid "Spam" msgstr "Spam" @@ -222,7 +222,7 @@ msgstr "Spam" msgid "Non spam" msgstr "Non spam" -#: agenda_culturel/filters.py:489 agenda_culturel/models.py:2464 +#: agenda_culturel/filters.py:489 agenda_culturel/models.py:2477 msgid "Type" msgstr "Type" @@ -253,11 +253,11 @@ msgstr "" msgid "Your email" msgstr "Votre adresse email" -#: agenda_culturel/forms.py:168 agenda_culturel/models.py:2431 +#: agenda_culturel/forms.py:168 agenda_culturel/models.py:2444 msgid "Your email address" msgstr "Votre adresse email" -#: agenda_culturel/forms.py:174 agenda_culturel/models.py:2456 +#: agenda_culturel/forms.py:174 agenda_culturel/models.py:2469 msgid "Comments" msgstr "Commentaires" @@ -273,19 +273,19 @@ msgid "Receive notification of publication or leave a message for moderation" msgstr "Être notifié de la publication ou laisser un message à la modération" #: agenda_culturel/forms.py:212 agenda_culturel/models.py:312 -#: agenda_culturel/models.py:880 agenda_culturel/models.py:2610 -#: agenda_culturel/models.py:2721 +#: agenda_culturel/models.py:875 agenda_culturel/models.py:2623 +#: agenda_culturel/models.py:2734 msgid "Category" msgstr "Catégorie" #: agenda_culturel/forms.py:218 agenda_culturel/forms.py:251 #: agenda_culturel/forms.py:297 agenda_culturel/forms.py:481 -#: agenda_culturel/models.py:366 agenda_culturel/models.py:997 +#: agenda_culturel/models.py:366 agenda_culturel/models.py:992 msgid "Tags" msgstr "Étiquettes" #: agenda_culturel/forms.py:226 agenda_culturel/forms.py:646 -#: agenda_culturel/models.py:1127 +#: agenda_culturel/models.py:1117 msgid "Event" msgstr "Événement" @@ -328,13 +328,13 @@ msgstr "Cet événement est récurrent" msgid "Details" msgstr "Détails" -#: agenda_culturel/forms.py:387 agenda_culturel/models.py:909 -#: agenda_culturel/models.py:2585 +#: agenda_culturel/forms.py:387 agenda_culturel/models.py:904 +#: agenda_culturel/models.py:2598 msgid "Location" msgstr "Localisation" #: agenda_culturel/forms.py:392 agenda_culturel/models.py:110 -#: agenda_culturel/models.py:953 +#: agenda_culturel/models.py:948 msgid "Illustration" msgstr "Illustration" @@ -358,9 +358,13 @@ msgstr "L'heure de fin ne peut pas être avant l'heure de début." msgid "Select tags from existing ones." msgstr "Sélectionner des étiquettes depuis celles existantes." +#: agenda_culturel/forms.py:541 +msgid "Data" +msgstr "" + #: agenda_culturel/forms.py:543 -msgid "JSON in the format expected for the import." -msgstr "JSON dans le format attendu pour l'import" +msgid "Supported formats: json, html." +msgstr "" #: agenda_culturel/forms.py:565 msgid " (locally modified version)" @@ -408,8 +412,8 @@ msgstr "Valeur de la version sélectionnée" msgid "Apply category {} to the event {}" msgstr "Appliquer la catégorie {} à l'événement {}" -#: agenda_culturel/forms.py:872 agenda_culturel/models.py:697 -#: agenda_culturel/models.py:2773 +#: agenda_culturel/forms.py:872 agenda_culturel/models.py:692 +#: agenda_culturel/models.py:2786 msgid "Place" msgstr "Lieu" @@ -431,7 +435,7 @@ msgstr "" msgid "Header" msgstr "Entête" -#: agenda_culturel/forms.py:932 agenda_culturel/models.py:659 +#: agenda_culturel/forms.py:932 agenda_culturel/models.py:654 msgid "Address" msgstr "Adresse" @@ -447,7 +451,7 @@ msgstr "Informations" msgid "Add a comment" msgstr "Ajouter un commentaire" -#: agenda_culturel/forms.py:1018 agenda_culturel/models.py:2874 +#: agenda_culturel/forms.py:1018 agenda_culturel/models.py:2887 msgid "Period type" msgstr "Type de période" @@ -455,7 +459,7 @@ msgstr "Type de période" msgid "ICAL file" msgstr "Fichier ICAL" -#: agenda_culturel/import_tasks/extractor.py:226 +#: agenda_culturel/import_tasks/extractor.py:229 msgid "Unknown title" msgstr "Titre inconnu" @@ -561,9 +565,9 @@ msgid "User profiles" msgstr "" #: agenda_culturel/models.py:195 agenda_culturel/models.py:242 -#: agenda_culturel/models.py:321 agenda_culturel/models.py:616 -#: agenda_culturel/models.py:657 agenda_culturel/models.py:763 -#: agenda_culturel/models.py:2423 agenda_culturel/models.py:2535 +#: agenda_culturel/models.py:321 agenda_culturel/models.py:611 +#: agenda_culturel/models.py:652 agenda_culturel/models.py:758 +#: agenda_culturel/models.py:2436 agenda_culturel/models.py:2548 msgid "Name" msgstr "Nom" @@ -623,8 +627,8 @@ msgstr "Catégories" msgid "Tag name" msgstr "Nom de l'étiquette" -#: agenda_culturel/models.py:329 agenda_culturel/models.py:680 -#: agenda_culturel/models.py:779 agenda_culturel/models.py:936 +#: agenda_culturel/models.py:329 agenda_culturel/models.py:675 +#: agenda_culturel/models.py:774 agenda_culturel/models.py:931 msgid "Description" msgstr "Description" @@ -632,8 +636,8 @@ msgstr "Description" msgid "Description of the tag" msgstr "Description de l'étiquette" -#: agenda_culturel/models.py:336 agenda_culturel/models.py:2390 -#: agenda_culturel/models.py:2437 +#: agenda_culturel/models.py:336 agenda_culturel/models.py:2403 +#: agenda_culturel/models.py:2450 msgid "Message" msgstr "Message" @@ -696,50 +700,50 @@ msgstr "" msgid "Duplicated events" msgstr "Événements dupliqués" -#: agenda_culturel/models.py:617 +#: agenda_culturel/models.py:612 msgid "Name of the location" msgstr "Nom de la position" -#: agenda_culturel/models.py:628 +#: agenda_culturel/models.py:623 msgid "Main" msgstr "Principale" -#: agenda_culturel/models.py:630 +#: agenda_culturel/models.py:625 msgid "This location is one of the main locations (shown first higher values)." msgstr "" "Cette position est une position principale (affichage en premier des plus " "grandes valeurs)." -#: agenda_culturel/models.py:635 +#: agenda_culturel/models.py:630 msgid "Suggested distance (km)" msgstr "" -#: agenda_culturel/models.py:637 +#: agenda_culturel/models.py:632 msgid "" "If this distance is given, this location is part of the suggested filters." msgstr "" -#: agenda_culturel/models.py:646 +#: agenda_culturel/models.py:641 msgid "Reference location" msgstr "Position de référence" -#: agenda_culturel/models.py:647 +#: agenda_culturel/models.py:642 msgid "Reference locations" msgstr "Positions de référence" -#: agenda_culturel/models.py:657 +#: agenda_culturel/models.py:652 msgid "Name of the place" msgstr "Nom du lieu" -#: agenda_culturel/models.py:660 +#: agenda_culturel/models.py:655 msgid "Address of this place (without city name)" msgstr "Adresse de ce lieu (sans le nom de la ville)" -#: agenda_culturel/models.py:665 +#: agenda_culturel/models.py:660 msgid "Postcode" msgstr "Code postal" -#: agenda_culturel/models.py:667 +#: agenda_culturel/models.py:662 msgid "" "The post code is not displayed, but makes it easier to find an address when " "you enter it." @@ -747,23 +751,23 @@ msgstr "" "Le code postal ne sera pas affiché, mais facilite la recherche d'adresse au " "moment de la saisie." -#: agenda_culturel/models.py:672 +#: agenda_culturel/models.py:667 msgid "City" msgstr "Ville" -#: agenda_culturel/models.py:672 +#: agenda_culturel/models.py:667 msgid "City name" msgstr "Nom de la ville" -#: agenda_culturel/models.py:681 +#: agenda_culturel/models.py:676 msgid "Description of the place, including accessibility." msgstr "Description du lieu, inclus l'accessibilité." -#: agenda_culturel/models.py:688 +#: agenda_culturel/models.py:683 msgid "Alternative names" msgstr "Noms alternatifs" -#: agenda_culturel/models.py:690 +#: agenda_culturel/models.py:685 msgid "" "Alternative names or addresses used to match a place with the free-form " "location of an event." @@ -771,31 +775,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:698 +#: agenda_culturel/models.py:693 msgid "Places" msgstr "Lieux" -#: agenda_culturel/models.py:764 +#: agenda_culturel/models.py:759 msgid "Organisation name" msgstr "Nom de l'organisme" -#: agenda_culturel/models.py:771 +#: agenda_culturel/models.py:766 msgid "Website" msgstr "Site internet" -#: agenda_culturel/models.py:772 +#: agenda_culturel/models.py:767 msgid "Website of the organisation" msgstr "Site internet de l'organisme" -#: agenda_culturel/models.py:780 +#: agenda_culturel/models.py:775 msgid "Description of the organisation." msgstr "Description de l'organisme" -#: agenda_culturel/models.py:787 +#: agenda_culturel/models.py:782 msgid "Principal place" msgstr "Lieu principal" -#: agenda_culturel/models.py:789 +#: agenda_culturel/models.py:784 msgid "" "Place mainly associated with this organizer. Mainly used if there is a " "similarity in the name, to avoid redundant displays." @@ -803,75 +807,75 @@ msgstr "" "Lieu principalement associé à cette organisation. Principalement utilisé " "s'il y a une similarité de nom, pour éviter les affichages redondants." -#: agenda_culturel/models.py:797 +#: agenda_culturel/models.py:792 msgid "Organisation" msgstr "Organisme" -#: agenda_culturel/models.py:798 +#: agenda_culturel/models.py:793 msgid "Organisations" msgstr "Organismes" -#: agenda_culturel/models.py:809 agenda_culturel/models.py:2580 +#: agenda_culturel/models.py:804 agenda_culturel/models.py:2593 msgid "Published" msgstr "Publié" -#: agenda_culturel/models.py:810 +#: agenda_culturel/models.py:805 msgid "Draft" msgstr "Brouillon" -#: agenda_culturel/models.py:811 +#: agenda_culturel/models.py:806 msgid "Trash" msgstr "Corbeille" -#: agenda_culturel/models.py:821 +#: agenda_culturel/models.py:816 msgid "Author currently editing/moderating the event" msgstr "" -#: agenda_culturel/models.py:831 +#: agenda_culturel/models.py:826 msgid "Author of the event creation" msgstr "Auteur de la création de l'événement" -#: agenda_culturel/models.py:840 +#: agenda_culturel/models.py:835 msgid "Author of the last importation" msgstr "Auteur de la dernière importation" -#: agenda_culturel/models.py:849 +#: agenda_culturel/models.py:844 msgid "Author of the last modification" msgstr "Auteur de la dernière modification" -#: agenda_culturel/models.py:858 +#: agenda_culturel/models.py:853 msgid "Author of the last moderation" msgstr "Auteur de la dernière modération" -#: agenda_culturel/models.py:869 +#: agenda_culturel/models.py:864 msgid "Title" msgstr "Titre" -#: agenda_culturel/models.py:886 agenda_culturel/models.py:2859 +#: agenda_culturel/models.py:881 agenda_culturel/models.py:2872 msgid "Start day" msgstr "Date de début" -#: agenda_culturel/models.py:888 +#: agenda_culturel/models.py:883 msgid "Start time" msgstr "Heure de début" -#: agenda_culturel/models.py:894 agenda_culturel/models.py:2860 +#: agenda_culturel/models.py:889 agenda_culturel/models.py:2873 msgid "End day" msgstr "Date de fin" -#: agenda_culturel/models.py:898 +#: agenda_culturel/models.py:893 msgid "End time" msgstr "Heure de fin" -#: agenda_culturel/models.py:901 +#: agenda_culturel/models.py:896 msgid "Recurrence" msgstr "Récurrence" -#: agenda_culturel/models.py:915 +#: agenda_culturel/models.py:910 msgid "Location (free form)" msgstr "Localisation (forme libre)" -#: agenda_culturel/models.py:917 +#: agenda_culturel/models.py:912 msgid "" "Address of the event in case its not available in the already known places " "(free form)" @@ -879,11 +883,11 @@ msgstr "" "Adresse d'un événement si elle n'est pas déjà présente dans la liste des " "lieux disponibles (forme libre)" -#: agenda_culturel/models.py:926 +#: agenda_culturel/models.py:921 msgid "Local event" msgstr "Événement de portée locale" -#: agenda_culturel/models.py:928 +#: agenda_culturel/models.py:923 msgid "" "If the event is a local event, it will not be proposed by default in daily, " "weekly or monthly views, unless the user has explicitly indicated that he " @@ -893,11 +897,11 @@ msgstr "" "les vues quotidiennes, hebdomadaires ou mensuelles, sauf si l'utilisateur a " "explicitement indiqué qu'il souhaite voir ces événements." -#: agenda_culturel/models.py:944 +#: agenda_culturel/models.py:939 msgid "Organisers" msgstr "Organisations" -#: agenda_culturel/models.py:946 +#: agenda_culturel/models.py:941 msgid "" "list of event organisers. Organizers will only be displayed if one of them " "does not normally use the venue." @@ -905,259 +909,259 @@ msgstr "" "Liste des organisations de l'événements. Les organisations seront affichés " "uniquement si au moins un d'entre eux n'utilise pas habituellement le lieu." -#: agenda_culturel/models.py:960 +#: agenda_culturel/models.py:955 msgid "Illustration (URL)" msgstr "Illustration (URL)" -#: agenda_culturel/models.py:961 +#: agenda_culturel/models.py:956 msgid "External URL of the illustration image" msgstr "URL externe de l'image illustrative" -#: agenda_culturel/models.py:967 +#: agenda_culturel/models.py:962 msgid "Illustration description" msgstr "Description de l'illustration" -#: agenda_culturel/models.py:968 +#: agenda_culturel/models.py:963 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:976 +#: agenda_culturel/models.py:971 msgid "Importation source" msgstr "Source d'importation" -#: agenda_culturel/models.py:977 +#: agenda_culturel/models.py:972 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:983 +#: agenda_culturel/models.py:978 msgid "UUIDs" msgstr "UUIDs" -#: agenda_culturel/models.py:984 +#: agenda_culturel/models.py:979 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:990 +#: agenda_culturel/models.py:985 msgid "Online sources or ticketing" msgstr "Sources en ligne ou billetterie" -#: agenda_culturel/models.py:1004 +#: agenda_culturel/models.py:999 msgid "Other versions" msgstr "" -#: agenda_culturel/models.py:1128 +#: agenda_culturel/models.py:1118 msgid "Events" msgstr "Événements" -#: agenda_culturel/models.py:1289 +#: agenda_culturel/models.py:1302 msgid "recurrent import" msgstr "import récurrent" -#: agenda_culturel/models.py:1291 +#: agenda_culturel/models.py:1304 msgid "a non authenticated user" msgstr "un utilisateur non connecté" -#: agenda_culturel/models.py:1813 +#: agenda_culturel/models.py:1826 msgid "Your event has been published" msgstr "Ton événement a été publié" -#: agenda_culturel/models.py:1818 +#: agenda_culturel/models.py:1831 msgid "Your message has not been retained" msgstr "Ton événement n'a pas été retenu" -#: agenda_culturel/models.py:2378 +#: agenda_culturel/models.py:2391 msgid "From contributor" msgstr "D'un·e contributeurice" -#: agenda_culturel/models.py:2379 +#: agenda_culturel/models.py:2392 msgid "Import process" msgstr "Processus d'import" -#: agenda_culturel/models.py:2380 +#: agenda_culturel/models.py:2393 msgid "Update process" msgstr "Processus de mise à jour" -#: agenda_culturel/models.py:2381 +#: agenda_culturel/models.py:2394 msgid "Contact form" msgstr "Formulaire de contact" -#: agenda_culturel/models.py:2382 +#: agenda_culturel/models.py:2395 msgid "Event report" msgstr "Signalemet d'événement" -#: agenda_culturel/models.py:2385 +#: agenda_culturel/models.py:2398 msgid "From contributor (without message)" msgstr "D'un·e contributeurice (sans message)" -#: agenda_culturel/models.py:2391 +#: agenda_culturel/models.py:2404 msgid "Messages" msgstr "Messages" -#: agenda_culturel/models.py:2400 +#: agenda_culturel/models.py:2413 msgid "Subject" msgstr "Sujet" -#: agenda_culturel/models.py:2401 +#: agenda_culturel/models.py:2414 msgid "The subject of your message" msgstr "Sujet de votre message" -#: agenda_culturel/models.py:2407 +#: agenda_culturel/models.py:2420 msgid "Related event" msgstr "Événement associé" -#: agenda_culturel/models.py:2408 +#: agenda_culturel/models.py:2421 msgid "The message is associated with this event." msgstr "Le message est associé à cet événement." -#: agenda_culturel/models.py:2416 +#: agenda_culturel/models.py:2429 msgid "Author of the message" msgstr "Auteur du message" -#: agenda_culturel/models.py:2424 +#: agenda_culturel/models.py:2437 msgid "Your name" msgstr "Votre nom" -#: agenda_culturel/models.py:2430 +#: agenda_culturel/models.py:2443 msgid "Email address" msgstr "Adresse email" -#: agenda_culturel/models.py:2437 +#: agenda_culturel/models.py:2450 msgid "Your message" msgstr "Votre message" -#: agenda_culturel/models.py:2444 +#: agenda_culturel/models.py:2457 msgid "This message is a spam." msgstr "Ce message est un spam." -#: agenda_culturel/models.py:2451 +#: agenda_culturel/models.py:2464 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:2457 +#: agenda_culturel/models.py:2470 msgid "Comments on the message from the moderation team" msgstr "Commentaires sur ce message par l'équipe de modération" -#: agenda_culturel/models.py:2490 agenda_culturel/models.py:2668 +#: agenda_culturel/models.py:2503 agenda_culturel/models.py:2681 msgid "Recurrent import" msgstr "Import récurrent" -#: agenda_culturel/models.py:2491 +#: agenda_culturel/models.py:2504 msgid "Recurrent imports" msgstr "Imports récurrents" -#: agenda_culturel/models.py:2495 +#: agenda_culturel/models.py:2508 msgid "ical" msgstr "ical" -#: agenda_culturel/models.py:2496 +#: agenda_culturel/models.py:2509 msgid "ical no busy" msgstr "ical sans busy" -#: agenda_culturel/models.py:2497 +#: agenda_culturel/models.py:2510 msgid "ical no VC" msgstr "ical sans VC" -#: agenda_culturel/models.py:2498 +#: agenda_culturel/models.py:2511 msgid "ical naive timezone" msgstr "ical timezone naïve" -#: agenda_culturel/models.py:2499 +#: agenda_culturel/models.py:2512 msgid "lacoope.org" msgstr "lacoope.org" -#: agenda_culturel/models.py:2500 +#: agenda_culturel/models.py:2513 msgid "la comédie" msgstr "la comédie" -#: agenda_culturel/models.py:2501 +#: agenda_culturel/models.py:2514 msgid "le fotomat" msgstr "le fotomat" -#: agenda_culturel/models.py:2502 +#: agenda_culturel/models.py:2515 msgid "la puce à l'oreille" msgstr "la puce à loreille" -#: agenda_culturel/models.py:2503 +#: agenda_culturel/models.py:2516 msgid "Plugin wordpress MEC" msgstr "Plugin wordpress MEC" -#: agenda_culturel/models.py:2504 +#: agenda_culturel/models.py:2517 msgid "Événements d'une page FB" msgstr "Événements d'une page FB" -#: agenda_culturel/models.py:2505 +#: agenda_culturel/models.py:2518 msgid "Billetterie Clermont-Ferrand" msgstr "" -#: agenda_culturel/models.py:2506 +#: agenda_culturel/models.py:2519 msgid "Arachnée concert" msgstr "Arachnée concert" -#: agenda_culturel/models.py:2507 +#: agenda_culturel/models.py:2520 msgid "Le Rio" msgstr "Le Rio" -#: agenda_culturel/models.py:2508 +#: agenda_culturel/models.py:2521 msgid "La Raymonde" msgstr "La Raymone" -#: agenda_culturel/models.py:2509 +#: agenda_culturel/models.py:2522 msgid "Agenda apidae tourisme" msgstr "Agenda apidae tourisme" -#: agenda_culturel/models.py:2510 +#: agenda_culturel/models.py:2523 msgid "Agenda iguana (médiathèques)" msgstr "Agenda iguana (médiathèques)" -#: agenda_culturel/models.py:2511 +#: agenda_culturel/models.py:2524 msgid "Mille formes" msgstr "Mille Formes" -#: agenda_culturel/models.py:2512 +#: agenda_culturel/models.py:2525 msgid "Les Amis du Temps des Cerises" msgstr "Les Amis du Temps des Cerises" -#: agenda_culturel/models.py:2513 +#: agenda_culturel/models.py:2526 msgid "Mobilizon" msgstr "Mobilizon" -#: agenda_culturel/models.py:2514 +#: agenda_culturel/models.py:2527 msgid "Le caméléon" msgstr "" -#: agenda_culturel/models.py:2515 +#: agenda_culturel/models.py:2528 msgid "Echosciences" msgstr "" -#: agenda_culturel/models.py:2516 +#: agenda_culturel/models.py:2529 msgid "Hello Asso" msgstr "Hello Asso" -#: agenda_culturel/models.py:2519 +#: agenda_culturel/models.py:2532 msgid "simple" msgstr "simple" -#: agenda_culturel/models.py:2520 +#: agenda_culturel/models.py:2533 msgid "Headless Chromium" msgstr "chromium sans interface" -#: agenda_culturel/models.py:2523 +#: agenda_culturel/models.py:2536 msgid "Headless Chromium (pause)" msgstr "chromium sans interface (pause)" -#: agenda_culturel/models.py:2529 +#: agenda_culturel/models.py:2542 msgid "daily" msgstr "chaque jour" -#: agenda_culturel/models.py:2531 +#: agenda_culturel/models.py:2544 msgid "weekly" msgstr "chaque semaine" -#: agenda_culturel/models.py:2532 +#: agenda_culturel/models.py:2545 msgid "never" msgstr "jamais" -#: agenda_culturel/models.py:2537 +#: agenda_culturel/models.py:2550 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." @@ -1165,151 +1169,151 @@ 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:2544 +#: agenda_culturel/models.py:2557 msgid "Processor" msgstr "Processeur" -#: agenda_culturel/models.py:2550 +#: agenda_culturel/models.py:2563 msgid "Downloader" msgstr "Téléchargeur" -#: agenda_culturel/models.py:2557 +#: agenda_culturel/models.py:2570 msgid "Import recurrence" msgstr "Récurrence d'import" -#: agenda_culturel/models.py:2564 +#: agenda_culturel/models.py:2577 msgid "Source" msgstr "Source" -#: agenda_culturel/models.py:2565 +#: agenda_culturel/models.py:2578 msgid "URL of the source document" msgstr "URL du document source" -#: agenda_culturel/models.py:2570 +#: agenda_culturel/models.py:2583 msgid "Browsable url" msgstr "URL navigable" -#: agenda_culturel/models.py:2572 +#: agenda_culturel/models.py:2585 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:2581 +#: agenda_culturel/models.py:2594 msgid "Status of each imported event (published or draft)" msgstr "Status de chaque événement importé (publié ou brouillon)" -#: agenda_culturel/models.py:2586 +#: agenda_culturel/models.py:2599 msgid "Address for each imported event" msgstr "Adresse de chaque événement importé" -#: agenda_culturel/models.py:2593 +#: agenda_culturel/models.py:2606 msgid "Force location" msgstr "Focer la localisation" -#: agenda_culturel/models.py:2594 +#: agenda_culturel/models.py:2607 msgid "force location even if another is detected." msgstr "Forcer la localisation même si une autre a été détectée." -#: agenda_culturel/models.py:2600 +#: agenda_culturel/models.py:2613 msgid "Organiser" msgstr "Organisation" -#: agenda_culturel/models.py:2601 +#: agenda_culturel/models.py:2614 msgid "Organiser of each imported event" msgstr "Organisme à l'origine de chaque événement importé" -#: agenda_culturel/models.py:2611 +#: agenda_culturel/models.py:2624 msgid "Category of each imported event" msgstr "Catégorie de chaque événement importé" -#: agenda_culturel/models.py:2619 +#: agenda_culturel/models.py:2632 msgid "Tags for each imported event" msgstr "Étiquettes de chaque événement importé" -#: agenda_culturel/models.py:2620 +#: agenda_culturel/models.py:2633 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:2649 +#: agenda_culturel/models.py:2662 msgid "Running" msgstr "En cours" -#: agenda_culturel/models.py:2650 +#: agenda_culturel/models.py:2663 msgid "Canceled" msgstr "Annulé" -#: agenda_culturel/models.py:2651 +#: agenda_culturel/models.py:2664 msgid "Success" msgstr "Succès" -#: agenda_culturel/models.py:2652 +#: agenda_culturel/models.py:2665 msgid "Failed" msgstr "Erreur" -#: agenda_culturel/models.py:2655 +#: agenda_culturel/models.py:2668 msgid "Batch importation" msgstr "Importation par lot" -#: agenda_culturel/models.py:2656 +#: agenda_culturel/models.py:2669 msgid "Batch importations" msgstr "Importations par lot" -#: agenda_culturel/models.py:2669 +#: agenda_culturel/models.py:2682 msgid "Reference to the recurrent import processing" msgstr "Référence du processus d'import récurrent" -#: agenda_culturel/models.py:2677 +#: agenda_culturel/models.py:2690 msgid "URL (if not recurrent import)" msgstr "URL (si pas d'import récurrent)" -#: agenda_culturel/models.py:2678 +#: agenda_culturel/models.py:2691 msgid "Source URL if no RecurrentImport is associated." msgstr "URL source si aucun import récurrent n'est associé" -#: agenda_culturel/models.py:2693 +#: agenda_culturel/models.py:2706 msgid "Error message" msgstr "Votre message" -#: agenda_culturel/models.py:2697 +#: agenda_culturel/models.py:2710 msgid "Number of collected events" msgstr "Nombre d'événements collectés" -#: agenda_culturel/models.py:2700 +#: agenda_culturel/models.py:2713 msgid "Number of imported events" msgstr "Nombre d'événements importés" -#: agenda_culturel/models.py:2703 +#: agenda_culturel/models.py:2716 msgid "Number of updated events" msgstr "Nombre d'événements mis à jour" -#: agenda_culturel/models.py:2706 +#: agenda_culturel/models.py:2719 msgid "Number of removed events" msgstr "Nombre d'événements supprimés" -#: agenda_culturel/models.py:2714 +#: agenda_culturel/models.py:2727 msgid "Weight" msgstr "Poids" -#: agenda_culturel/models.py:2715 +#: agenda_culturel/models.py:2728 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:2722 +#: agenda_culturel/models.py:2735 msgid "Category applied to the event" msgstr "Catégorie appliquée à l'événement" -#: agenda_culturel/models.py:2727 +#: agenda_culturel/models.py:2740 msgid "Contained in the title" msgstr "Contenu dans le titre" -#: agenda_culturel/models.py:2728 +#: agenda_culturel/models.py:2741 msgid "Text contained in the event title" msgstr "Texte contenu dans le titre de l'événement" -#: agenda_culturel/models.py:2734 +#: agenda_culturel/models.py:2747 msgid "Exact title extract" msgstr "Extrait exact du titre" -#: agenda_culturel/models.py:2736 +#: agenda_culturel/models.py:2749 msgid "" "If checked, the extract will be searched for in the title using the exact " "form (capitals, accents)." @@ -1317,19 +1321,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans le titre en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2742 +#: agenda_culturel/models.py:2755 msgid "Contained in the description" msgstr "Contenu dans la description" -#: agenda_culturel/models.py:2743 +#: agenda_culturel/models.py:2756 msgid "Text contained in the description" msgstr "Texte contenu dans la description" -#: agenda_culturel/models.py:2749 +#: agenda_culturel/models.py:2762 msgid "Exact description extract" msgstr "Extrait exact de description" -#: agenda_culturel/models.py:2751 +#: agenda_culturel/models.py:2764 msgid "" "If checked, the extract will be searched for in the description using the " "exact form (capitals, accents)." @@ -1337,19 +1341,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans la description en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:2757 +#: agenda_culturel/models.py:2770 msgid "Contained in the location" msgstr "Contenu dans la localisation" -#: agenda_culturel/models.py:2758 +#: agenda_culturel/models.py:2771 msgid "Text contained in the event location" msgstr "Texte contenu dans la localisation de l'événement" -#: agenda_culturel/models.py:2764 +#: agenda_culturel/models.py:2777 msgid "Exact location extract" msgstr "Extrait exact de localisation" -#: agenda_culturel/models.py:2766 +#: agenda_culturel/models.py:2779 msgid "" "If checked, the extract will be searched for in the location using the exact " "form (capitals, accents)." @@ -1357,51 +1361,51 @@ msgstr "" "Si coché, l'extrait sera recherché dans la localisation en utilisant la " "forme exacte (majuscules, accents)" -#: agenda_culturel/models.py:2774 +#: agenda_culturel/models.py:2787 msgid "Location from place" msgstr "Localisation depuis le lieu" -#: agenda_culturel/models.py:2783 +#: agenda_culturel/models.py:2796 msgid "Categorisation rule" msgstr "Règle de catégorisation" -#: agenda_culturel/models.py:2784 +#: agenda_culturel/models.py:2797 msgid "Categorisation rules" msgstr "Règles de catégorisation" -#: agenda_culturel/models.py:2858 +#: agenda_culturel/models.py:2871 msgid "Period name" msgstr "Nom de la période" -#: agenda_culturel/models.py:2863 +#: agenda_culturel/models.py:2876 msgid "Special period" msgstr "Période remarquable" -#: agenda_culturel/models.py:2864 +#: agenda_culturel/models.py:2877 msgid "Special periods" msgstr "Périodes remarquables" -#: agenda_culturel/models.py:2870 +#: agenda_culturel/models.py:2883 msgid "public holidays" msgstr "Jour férié" -#: agenda_culturel/models.py:2871 +#: agenda_culturel/models.py:2884 msgid "school vacations" msgstr "Vacances scolaires" -#: agenda_culturel/models.py:2889 +#: agenda_culturel/models.py:2902 msgid "The end date must be after or equal to the start date." msgstr "La date de fin doit être après ou identique à la date de début." -#: agenda_culturel/models.py:2897 +#: agenda_culturel/models.py:2910 msgid " on " msgstr " du " -#: agenda_culturel/models.py:2900 +#: agenda_culturel/models.py:2913 msgid " from " msgstr " du " -#: agenda_culturel/models.py:2900 +#: agenda_culturel/models.py:2913 msgid " to " msgstr " au " @@ -1651,15 +1655,19 @@ msgstr "Le cache a été vidé avec succès." msgid "Your user profile has been successfully modified." msgstr "Votre profil utilisateur a été modifié avec succès." -#: agenda_culturel/views/import_batch_views.py:77 -msgid "The import has been run successfully." -msgstr "L'import a été lancé avec succès" +#: agenda_culturel/views/import_batch_views.py:83 +msgid "The import from json has been run successfully." +msgstr "L'import depuis json a été lancé avec succès" -#: agenda_culturel/views/import_batch_views.py:99 +#: agenda_culturel/views/import_batch_views.py:90 +msgid "The import from html ({} detected links) has been run successfully." +msgstr "L'import depuis html ({} liens détectés) a été lancé avec succès" + +#: agenda_culturel/views/import_batch_views.py:113 msgid "The import has been canceled." msgstr "L'import a été annulé" -#: agenda_culturel/views/import_batch_views.py:122 +#: agenda_culturel/views/import_batch_views.py:136 msgid "The orphan event update has been launched." msgstr "La mise à jour de l'événement orphelin a été lancée." diff --git a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html index 074e5fb..76cec0d 100644 --- a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html @@ -4,12 +4,30 @@ {% block og_title %}Importation manuelle{% endblock %} {% endblock %} {% block content %} -

Importation manuelle

-
-
- {% csrf_token %} - {{ form.as_p }} - -
-
+
+
+
+

Importation manuelle

+
+

+ Le formulaire ci-dessous permet d'importer des événements par lot. Deux formats sont actuellement disponibles : +

+
    +
  • + Format json : format structuré tel qu'attendu par l'importateur. Voir la documentation sur le wiki du projet. +
  • +
  • + Format html : forme libre, où tous les liens qui sont considérés comme reconnus par l'outil d'import principal seront importés. + Cette fonctionnalité peut être utile quand on veut importer plus que les 8 événements détectés par défaut lors d'un import Facebook. Pour cela, se rendre sur la page de la liste + d'événements, enregistrer le html de la page (clic droit, enregistrer sous), puis copier le contenu de ce fichier html ci-dessous. +
  • +
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ {% include "agenda_culturel/side-nav.html" with current="manual-import" %} +
{% endblock %} diff --git a/src/agenda_culturel/templates/agenda_culturel/side-nav.html b/src/agenda_culturel/templates/agenda_culturel/side-nav.html index 79c93b1..15625b8 100644 --- a/src/agenda_culturel/templates/agenda_culturel/side-nav.html +++ b/src/agenda_culturel/templates/agenda_culturel/side-nav.html @@ -70,6 +70,10 @@ Historiques des importations +
  • + Import manuel +
  • {% endif %} {% if perms.agenda_culturel.view_recurrentimport %}
  • diff --git a/src/agenda_culturel/views/import_batch_views.py b/src/agenda_culturel/views/import_batch_views.py index 6b4b5c0..9609fe1 100644 --- a/src/agenda_culturel/views/import_batch_views.py +++ b/src/agenda_culturel/views/import_batch_views.py @@ -10,9 +10,12 @@ from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from ..celery import app as celery_app, update_orphan_pure_import_events -from ..celery import import_events_from_json +from ..celery import import_events_from_json, import_events_from_urls from ..forms import BatchImportationForm from ..models import Event, BatchImportation, RecurrentImport +from ..import_tasks.extractor import Extractor +from bs4 import BeautifulSoup +import json @login_required(login_url="/accounts/login/") @@ -72,9 +75,37 @@ def add_import(request): form = BatchImportationForm(request.POST) if form.is_valid(): - import_events_from_json.delay(form.data["json"]) + try: + # try to load data as a json file + json.loads(form.data["data"]) + # if data is a json, load it + import_events_from_json.delay(form.data["data"]) + messages.success( + request, _("The import from json has been run successfully.") + ) + except ValueError: + # otherwise, consider it as html, extract all urls, and import them + soup = BeautifulSoup(form.data["data"], "html.parser") + urls = list( + set( + [ + a["href"] + for a in soup.find_all("a", href=True) + if Extractor.is_known_url_default_extractors(a["href"]) + ] + ) + ) + # then import events from url + import_events_from_urls.delay( + urls, user_id=request.user.pk if request.user else None + ) + messages.success( + request, + _( + "The import from html ({} detected links) has been run successfully." + ).format(len(urls)), + ) - messages.success(request, _("The import has been run successfully.")) return HttpResponseRedirect(reverse_lazy("imports")) return render(request, "agenda_culturel/batchimportation_form.html", {"form": form}) From 2a99fec997f72f533eeca48e839499d6da014270 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Sun, 27 Apr 2025 23:24:03 +0200 Subject: [PATCH 10/18] Fix navigation (boutons pas cliquables partout) --- src/agenda_culturel/static/style.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index 7021d67..a1186e5 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -521,8 +521,9 @@ header .title { } - .slider-button { + .slider-button a, .slider-button-inside { @extend [role="button"]; + color: var(--primary-inverse); height: 1.8em; width: 1.8em; padding: 0; @@ -531,9 +532,6 @@ header .title { border-radius: .9em; line-height: 1.8em; text-align: center; - a { - color: var(--primary-inverse); - } } .slider-button.hidden { display: none; From 89092a1c2894477397eec5ffa812c18835b199a8 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 10:50:08 +0200 Subject: [PATCH 11/18] =?UTF-8?q?Am=C3=A9lioration=20de=20la=20gestion=20d?= =?UTF-8?q?es=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deployment/scripts/backend/start.sh | 2 +- deployment/scripts/nginx/nginx.conf | 8 +++++--- docker-compose.prod.yml | 2 ++ src/agenda_culturel/settings/base.py | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/deployment/scripts/backend/start.sh b/deployment/scripts/backend/start.sh index 2797011..814f73a 100755 --- a/deployment/scripts/backend/start.sh +++ b/deployment/scripts/backend/start.sh @@ -12,5 +12,5 @@ else python manage.py migrate --noinput python manage.py collectstatic --noinput python manage.py compilemessages - gunicorn "$APP_NAME".wsgi:application --bind "$APP_HOST":"$APP_PORT" --workers 3 --log-level=info + gunicorn "$APP_NAME".wsgi:application --bind "$APP_HOST":"$APP_PORT" --workers 5 --log-level=info fi diff --git a/deployment/scripts/nginx/nginx.conf b/deployment/scripts/nginx/nginx.conf index 50e7783..6b6b3c4 100644 --- a/deployment/scripts/nginx/nginx.conf +++ b/deployment/scripts/nginx/nginx.conf @@ -35,9 +35,11 @@ http { error_page 502 /static/html/502.html; error_page 503 /static/html/503.html; - if ($http_user_agent ~* "Amazonbot|meta-externalagent|ClaudeBot|ahrefsbot|semrushbot") { - return 444; - } + if ($http_user_agent ~* "Amazonbot|meta-externalagent|ClaudeBot|ahrefsbot|semrushbot") { + return 444; + } } + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 8451e5f..3ca36a9 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -75,6 +75,7 @@ services: - ./deployment/scripts/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - static_files:/usr/src/app/static - media_files:/usr/src/app/media + - log_files:/var/log/nginx env_file: .env.prod ports: - 6380:80 @@ -86,3 +87,4 @@ volumes: media_files: postgres_data_dir: redis_data: + log_files: diff --git a/src/agenda_culturel/settings/base.py b/src/agenda_culturel/settings/base.py index 2baf7db..d2149f2 100644 --- a/src/agenda_culturel/settings/base.py +++ b/src/agenda_culturel/settings/base.py @@ -297,9 +297,11 @@ LOGGING = { "disable_existing_loggers": False, "handlers": { "file": { + "class": "logging.handlers.RotatingFileHandler", "level": level_debug, - "class": "logging.FileHandler", "filename": "backend.log", + "maxBytes": 5 * 1024 * 1024, # 5 MB + "backupCount": 5, # keep last 5 files }, "mail_admins": { "level": "ERROR", From bfcc23c1334480ebffb972416ceb5dea3923e021 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 11:25:12 +0200 Subject: [PATCH 12/18] On supprime les logs nginx dans la console --- deployment/scripts/nginx/nginx.conf | 7 +++++-- docker-compose.prod.yml | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/deployment/scripts/nginx/nginx.conf b/deployment/scripts/nginx/nginx.conf index 6b6b3c4..986c2f7 100644 --- a/deployment/scripts/nginx/nginx.conf +++ b/deployment/scripts/nginx/nginx.conf @@ -8,6 +8,9 @@ http { gzip on; gzip_types text/plain text/css text/javascript; + log_format main 'remote_addr -remote_user [time_local] "request" ' + 'statusbody_bytes_sent "http_referer" ' + '"http_user_agent" "$http_x_forwarded_for"'; upstream backend { server backend:8000; @@ -38,8 +41,8 @@ http { if ($http_user_agent ~* "Amazonbot|meta-externalagent|ClaudeBot|ahrefsbot|semrushbot") { return 444; } + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; } - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log warn; } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 3ca36a9..eff08a0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -77,6 +77,7 @@ services: - media_files:/usr/src/app/media - log_files:/var/log/nginx env_file: .env.prod + command: /bin/sh -c "rm /var/log/nginx/access.log /var/log/nginx/error.log && nginx -g 'daemon off;'" ports: - 6380:80 depends_on: From c3fbaac38513b7b45298a7f082323baf9823e67e Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 12:05:06 +0200 Subject: [PATCH 13/18] Ajout d'un logrotate pour les logs de nginx --- .gitignore | 2 ++ deployment/Dockerfile-nginx | 18 ++++++++++++++++++ deployment/scripts/nginx/entrypoint.sh | 4 ++++ deployment/scripts/nginx/nginx.logrotate | 14 ++++++++++++++ docker-compose.prod.yml | 9 ++++----- 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 deployment/Dockerfile-nginx create mode 100644 deployment/scripts/nginx/entrypoint.sh create mode 100644 deployment/scripts/nginx/nginx.logrotate diff --git a/.gitignore b/.gitignore index d88bdbb..c1601d0 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ cover/ *.mo *.pot *.log +*.log.* local_settings.py db.sqlite3 db.sqlite3-journal @@ -89,6 +90,7 @@ letsencrypt/ experimentations/cache/ experimentations/cache-augustes.ical experimentations/events-augustes.json +logs-nginx # MacOS .DS_Store diff --git a/deployment/Dockerfile-nginx b/deployment/Dockerfile-nginx new file mode 100644 index 0000000..db9d9a3 --- /dev/null +++ b/deployment/Dockerfile-nginx @@ -0,0 +1,18 @@ +FROM nginx:latest + +RUN apt-get update && \ + apt-get install -y logrotate cron && \ + rm -rf /var/lib/apt/lists/* + +RUN rm -f /var/log/nginx/access.log /var/log/nginx/error.log +RUN chmod 755 /var/log/nginx +RUN chown root:nginx /var/log/nginx + +COPY deployment/scripts/nginx/nginx.logrotate /etc/logrotate.d/nginx +RUN chmod 644 /etc/logrotate.d/nginx + + +COPY deployment/scripts/nginx/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +CMD ["/entrypoint.sh"] diff --git a/deployment/scripts/nginx/entrypoint.sh b/deployment/scripts/nginx/entrypoint.sh new file mode 100644 index 0000000..dbd2a54 --- /dev/null +++ b/deployment/scripts/nginx/entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cron + +nginx -g 'daemon off;' diff --git a/deployment/scripts/nginx/nginx.logrotate b/deployment/scripts/nginx/nginx.logrotate new file mode 100644 index 0000000..17d3f4c --- /dev/null +++ b/deployment/scripts/nginx/nginx.logrotate @@ -0,0 +1,14 @@ +/var/log/nginx/*.log { + su root nginx + daily + missingok + rotate 7 + compress + delaycompress + notifempty + create 640 root nginx + sharedscripts + postrotate + [ -f /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid` + endscript +} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index eff08a0..bb96697 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,5 +1,3 @@ -version: '3.7' - services: backend: container_name: "${APP_NAME}-backend" @@ -69,15 +67,16 @@ services: command: [ "/bin/bash", "/app/deployment/scripts/wait-db.sh", "/app/deployment/scripts/celery/start-beat.sh" ] nginx: - image: nginx:latest container_name: "${APP_NAME}-nginx" + build: + context: . + dockerfile: deployment/Dockerfile-nginx volumes: - ./deployment/scripts/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - static_files:/usr/src/app/static - media_files:/usr/src/app/media - - log_files:/var/log/nginx + - ./logs-nginx:/var/log/nginx env_file: .env.prod - command: /bin/sh -c "rm /var/log/nginx/access.log /var/log/nginx/error.log && nginx -g 'daemon off;'" ports: - 6380:80 depends_on: From da5d5679fead1ade0961808989a6b274ed619186 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 12:28:48 +0200 Subject: [PATCH 14/18] Restore version (for compatibility) --- docker-compose.prod.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index bb96697..019dfbb 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,3 +1,5 @@ +version: '3.7' + services: backend: container_name: "${APP_NAME}-backend" From 2418a8f12bf2396d1084f0b5fe76ed4cfdf44d0d Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 14:52:21 +0200 Subject: [PATCH 15/18] =?UTF-8?q?Am=C3=A9lioration=20des=20imports=20multi?= =?UTF-8?q?ples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #417 --- src/agenda_culturel/celery.py | 55 ++++++++++++------- src/agenda_culturel/forms.py | 19 +++++++ .../batchimportation_form.html | 34 ++++++++++++ .../views/import_batch_views.py | 3 +- 4 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/agenda_culturel/celery.py b/src/agenda_culturel/celery.py index 77965a7..efff501 100644 --- a/src/agenda_culturel/celery.py +++ b/src/agenda_culturel/celery.py @@ -458,10 +458,24 @@ def import_events_from_url( from .db_importer import DBImporterEvents if isinstance(urls, list): - url = urls[0] - is_list = True + if len(urls) > 0 and isinstance(urls[0], list): + url = urls[0][0][0] + cat = urls[0][0][1] + tags = urls[0][0][2] + if isinstance(tags, str): + tags = [tags] + user_id = urls[1] + email = urls[2] + comments = urls[3] + is_tuple = True + is_list = False + else: + url = urls[0] + is_list = True + is_tuple = False else: is_list = False + is_tuple = False url = urls with memcache_chromium_lock(self.app.oid) as acquired: @@ -489,7 +503,8 @@ def import_events_from_url( try: ## create loader - u2e = URL2Events(ChromiumHeadlessDownloader(), single_event=True) + self.chromiumDownloader.pause = True + u2e = URL2Events(self.chromiumDownloader, single_event=True) # set default values values = {} if cat is not None: @@ -528,7 +543,11 @@ def import_events_from_url( logger.error(e) close_import_task(self.request.id, False, e, importer) - return urls[1:] if is_list else True + return ( + urls[1:] + if is_list + else [urls[0][1:], user_id, email, comments] if is_tuple else True + ) # if chromium is locked, we wait 30 seconds before retrying raise self.retry(countdown=30) @@ -538,22 +557,20 @@ def import_events_from_url( def import_events_from_urls( self, urls_cat_tags, user_id=None, email=None, comments=None ): - for ucat in urls_cat_tags: - if ucat is not None: - url = None - cat = None - tags = None - if isinstance(ucat, str): - url = ucat - elif isinstance(ucat, (list, tuple)): - url = ucat[0] - cat = ucat[1] - tags = ucat[2] - if url is not None: - import_events_from_url.delay( - url, cat, tags, user_id=user_id, email=email, comments=comments - ) + print("ça chaine baby") + # run tasks as a chain + tasks = chain( + ( + import_events_from_url.s( + [urls_cat_tags, user_id, email, comments], force=True + ) + if i == 0 + else import_events_from_url.s(force=True) + ) + for i in range(len(urls_cat_tags)) + ) + tasks.delay() @app.task(base=ChromiumTask, bind=True) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index c8f3f60..5e93fe0 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -544,6 +544,25 @@ class BatchImportationForm(Form): required=True, ) + category = ModelChoiceField( + label=_("Category"), + queryset=Category.objects.all().order_by("name"), + help_text=_("Used only if data is html."), + initial=None, + required=False, + ) + tags = MultipleChoiceField( + label=_("Tags"), + initial=None, + choices=[], + help_text=_("Used only if data is html."), + required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["tags"].choices = Tag.get_tag_groups(all=True) + class FixDuplicates(Form): required_css_class = "required" diff --git a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html index 76cec0d..8b7f891 100644 --- a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html @@ -22,6 +22,9 @@ d'événements, enregistrer le html de la page (clic droit, enregistrer sous), puis copier le contenu de ce fichier html ci-dessous.
  • +

    + Les champs catégorie et étiquettes seront utilisés si on utilise le format html, pour catégoriser tous les événements qui seront importés. +

    {% csrf_token %} {{ form.as_p }} @@ -30,4 +33,35 @@ {% include "agenda_culturel/side-nav.html" with current="manual-import" %} + + {% endblock %} diff --git a/src/agenda_culturel/views/import_batch_views.py b/src/agenda_culturel/views/import_batch_views.py index 9609fe1..eda99eb 100644 --- a/src/agenda_culturel/views/import_batch_views.py +++ b/src/agenda_culturel/views/import_batch_views.py @@ -97,7 +97,8 @@ def add_import(request): ) # then import events from url import_events_from_urls.delay( - urls, user_id=request.user.pk if request.user else None + [(u, form.data["category"], form.data["tags"]) for u in urls], + user_id=request.user.pk if request.user else None, ) messages.success( request, From 9ef4819eeaface7aa9b48faea1b360a5ba050511 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Mon, 28 Apr 2025 16:00:13 +0200 Subject: [PATCH 16/18] Ajout projets similaires --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 901b663..fd2ad63 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,10 @@ Si la migration de la base de données s'est bien executée, mais qu'aucun évé - `docker compose down --rmi all --volumes` - `make build-dev` + +### Projets similaires + +* [hackeragenda](https://github.com/YoloSwagTeam/hackeragenda) +* [mobilizon](https://framagit.org/kaihuri/mobilizon) +* [koalagator](https://github.com/koalagator/koalagator) +* [gathio](https://gath.io/) From 7bae10238b7c216c869430c0ac4cf9bc1658d246 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Tue, 29 Apr 2025 00:02:55 +0200 Subject: [PATCH 17/18] =?UTF-8?q?Nombre=20de=20propositions=20de=20tags=20?= =?UTF-8?q?=C3=A0=2010?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/agenda_culturel/batchimportation_form.html | 1 + src/agenda_culturel/templates/agenda_culturel/import.html | 3 ++- src/agenda_culturel/templates/agenda_culturel/import_set.html | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html index 8b7f891..653828f 100644 --- a/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/batchimportation_form.html @@ -56,6 +56,7 @@ { placeholderValue: 'Sélectionner les étiquettes à ajouter', allowHTML: true, + searchResultLimit: 10, delimiter: ',', removeItemButton: true, shouldSort: false, diff --git a/src/agenda_culturel/templates/agenda_culturel/import.html b/src/agenda_culturel/templates/agenda_culturel/import.html index caa16e2..7c921e4 100644 --- a/src/agenda_culturel/templates/agenda_culturel/import.html +++ b/src/agenda_culturel/templates/agenda_culturel/import.html @@ -11,7 +11,7 @@ {% block content %}
    -

    Importer un événement

    +

    Importer un événement

    {% url 'event_import_url' as local_url %} {% include "agenda_culturel/static_content.html" with name="import" url_path=local_url %}
    @@ -53,6 +53,7 @@ placeholderValue: 'Sélectionner les étiquettes à ajouter', allowHTML: true, delimiter: ',', + searchResultLimit: 10, removeItemButton: true, shouldSort: false, callbackOnCreateTemplates: () => (show_firstgroup) diff --git a/src/agenda_culturel/templates/agenda_culturel/import_set.html b/src/agenda_culturel/templates/agenda_culturel/import_set.html index 46415a0..53f8ac4 100644 --- a/src/agenda_culturel/templates/agenda_culturel/import_set.html +++ b/src/agenda_culturel/templates/agenda_culturel/import_set.html @@ -60,6 +60,7 @@ { placeholderValue: 'Sélectionner les étiquettes à ajouter', allowHTML: true, + searchResultLimit: 10, delimiter: ',', removeItemButton: true, shouldSort: false, From 095b64c9dbb2a7ac0a6fcff37e34a5c8fa399726 Mon Sep 17 00:00:00 2001 From: Jean-Marie Favreau Date: Tue, 29 Apr 2025 11:41:45 +0200 Subject: [PATCH 18/18] =?UTF-8?q?Am=C3=A9lioration=20rendu=20statistiques?= 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/statistics.html | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index a1186e5..1e6af49 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -677,10 +677,12 @@ header .remarque { display: inline-block; } -.highlight { +rect.ch-subdomain-bg.highlight { color: var(--primary); font-weight: bold; font-style: italic; + stroke-width: 2; + stroke: red !important; } .search .description { diff --git a/src/agenda_culturel/templates/agenda_culturel/statistics.html b/src/agenda_culturel/templates/agenda_culturel/statistics.html index d8d9f01..3d7ce17 100644 --- a/src/agenda_culturel/templates/agenda_culturel/statistics.html +++ b/src/agenda_culturel/templates/agenda_culturel/statistics.html @@ -132,7 +132,14 @@ }, theme: (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? "dark" : "light", itemSelector: '#cal-heatmap-startday', - animationDuration: 0 + animationDuration: 0, + scale: { + color: { + type: 'linear', + domain: [0, Math.max(...data_startday.map(d => d.value))], + range: ['#f0fefe', '#005e5e'], + }, + } }; // create first heatmap @@ -146,6 +153,12 @@ options.itemSelector = '#cal-heatmap-creation'; options.date.start = new Date("{{ first_by_creation.isoformat }}"); options.data.source = data_creation; + options.scale.color = { + type: 'linear', + domain: [0, Math.max(...data_creation.map(d => d.value))], + range: ['#fdf5f5', '#5e0000'], + }; + const cal_creation = new CalHeatmap(); cal_creation.paint(options, [ [CalendarLabel, calendarlabel],