diff --git a/src/agenda_culturel/admin.py b/src/agenda_culturel/admin.py index 64f1c33..9de4a23 100644 --- a/src/agenda_culturel/admin.py +++ b/src/agenda_culturel/admin.py @@ -10,7 +10,8 @@ from .models import ( RecurrentImport, Place, ContactMessage, - ReferenceLocation + ReferenceLocation, + Organisation ) from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget @@ -26,6 +27,7 @@ admin.site.register(RecurrentImport) admin.site.register(Place) admin.site.register(ContactMessage) admin.site.register(ReferenceLocation) +admin.site.register(Organisation) class URLWidget(DynamicArrayWidget): diff --git a/src/agenda_culturel/celery.py b/src/agenda_culturel/celery.py index fa79aa2..b435900 100644 --- a/src/agenda_culturel/celery.py +++ b/src/agenda_culturel/celery.py @@ -162,13 +162,14 @@ def run_recurrent_import_internal(rimport, downloader, req_id): location = rimport.defaultLocation tags = rimport.defaultTags published = rimport.defaultPublished + organisers = [] if rimport.defaultOrganiser is None else [rimport.defaultOrganiser.pk] try: # get events from website events = u2e.process( url, browsable_url, - default_values={"category": category, "location": location, "tags": tags}, + default_values={"category": category, "location": location, "tags": tags, "organisers": organisers}, published=published, ) diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index dd52e4f..41eaad1 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -186,6 +186,7 @@ class EventForm(ModelForm): super().__init__(*args, **kwargs) if not is_authenticated: del self.fields["status"] + del self.fields["organisers"] self.fields['category'].queryset = self.fields['category'].queryset.order_by('name') self.fields['category'].empty_label = None self.fields['category'].initial = Category.get_default_category() @@ -262,6 +263,7 @@ class EventModerateForm(ModelForm): fields = [ "status", "category", + "organisers", "exact_location", "tags" ] diff --git a/src/agenda_culturel/import_tasks/extractor.py b/src/agenda_culturel/import_tasks/extractor.py index e62ba2e..4822dad 100644 --- a/src/agenda_culturel/import_tasks/extractor.py +++ b/src/agenda_culturel/import_tasks/extractor.py @@ -187,6 +187,7 @@ class Extractor(ABC): "start_day": start_day, "uuids": uuids, "location": location if location else self.default_value_if_exists(default_values, "location"), + "organisers": self.default_value_if_exists(default_values, "organisers"), "description": description, "tags": tags + tags_default, "published": published, diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po index 3f9c7a4..3760be4 100644 --- a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po +++ b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: agenda_culturel\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-22 10:46+0100\n" +"POT-Creation-Date: 2024-11-22 23:02+0100\n" "PO-Revision-Date: 2023-10-29 14:16+0000\n" "Last-Translator: Jean-Marie Favreau \n" "Language-Team: Jean-Marie Favreau \n" @@ -110,7 +110,7 @@ msgstr "Non" msgid "Imported from" msgstr "Importé depuis" -#: agenda_culturel/filters.py:355 agenda_culturel/models.py:1579 +#: agenda_culturel/filters.py:355 agenda_culturel/models.py:1696 msgid "Closed" msgstr "Fermé" @@ -118,7 +118,7 @@ msgstr "Fermé" msgid "Open" msgstr "Ouvert" -#: agenda_culturel/filters.py:360 agenda_culturel/models.py:1573 +#: agenda_culturel/filters.py:360 agenda_culturel/models.py:1690 msgid "Spam" msgstr "Spam" @@ -142,8 +142,8 @@ msgstr "" "l'étiquette choisie." #: agenda_culturel/forms.py:88 agenda_culturel/models.py:170 -#: agenda_culturel/models.py:507 agenda_culturel/models.py:1683 -#: agenda_culturel/models.py:1788 +#: agenda_culturel/models.py:547 agenda_culturel/models.py:1811 +#: agenda_culturel/models.py:1916 msgid "Category" msgstr "Catégorie" @@ -153,7 +153,7 @@ msgstr "" "Facultatif. Si tu ne donnes pas la catégorie, on s'occupe de la trouver." #: agenda_culturel/forms.py:95 agenda_culturel/forms.py:143 -#: agenda_culturel/forms.py:248 agenda_culturel/models.py:613 +#: agenda_culturel/forms.py:249 agenda_culturel/models.py:662 msgid "Tags" msgstr "Étiquettes" @@ -165,23 +165,23 @@ msgstr "" "Facultatif. Si tu proposes des étiquettes, ça aidera les copaines à " "retrouver facilement ton événement." -#: agenda_culturel/forms.py:205 +#: agenda_culturel/forms.py:206 msgid "The end date must be after the start date." msgstr "La date de fin doit être après la date de début." -#: agenda_culturel/forms.py:221 +#: agenda_culturel/forms.py:222 msgid "The end time cannot be earlier than the start time." msgstr "L'heure de fin ne peut pas être avant l'heure de début." -#: agenda_culturel/forms.py:249 +#: agenda_culturel/forms.py:250 msgid "Select tags from existing ones." msgstr "Sélectionner des étiquettes depuis celles existantes." -#: agenda_culturel/forms.py:254 +#: agenda_culturel/forms.py:255 msgid "New tags" msgstr "Nouvelles étiquettes" -#: agenda_culturel/forms.py:255 +#: agenda_culturel/forms.py:256 msgid "" "Create new labels (sparingly). Note: by starting your tag with the " "characters “TW:”, youll create a “trigger warning” tag, and the associated " @@ -192,70 +192,70 @@ msgstr "" "étiquette “trigger warning”, et les événements associés seront annoncés " "comme tels." -#: agenda_culturel/forms.py:297 +#: agenda_culturel/forms.py:299 msgid "JSON in the format expected for the import." msgstr "JSON dans le format attendu pour l'import" -#: agenda_culturel/forms.py:319 +#: agenda_culturel/forms.py:321 msgid " (locally modified version)" msgstr " (version modifiée localement)" -#: agenda_culturel/forms.py:323 +#: agenda_culturel/forms.py:325 msgid " (synchronized on import version)" msgstr " (version synchronisée sur l'import)" -#: agenda_culturel/forms.py:327 +#: agenda_culturel/forms.py:329 msgid "Select {} as representative version." msgstr "Sélectionner {} comme version représentative" -#: agenda_culturel/forms.py:336 +#: agenda_culturel/forms.py:338 msgid "Update {} using some fields from other versions (interactive mode)." msgstr "" "Mettre à jour {} en utilisant quelques champs des autres versions (mode " "interactif)." -#: agenda_culturel/forms.py:343 +#: agenda_culturel/forms.py:345 msgid " Warning: a version is already locally modified." msgstr " Attention: une version a déjà été modifiée localement." -#: agenda_culturel/forms.py:348 +#: agenda_culturel/forms.py:350 msgid "Create a new version by merging (interactive mode)." msgstr "Créer une nouvelle version par fusion (mode interactif)." -#: agenda_culturel/forms.py:355 +#: agenda_culturel/forms.py:357 msgid "Make {} independent." msgstr "Rendre {} indépendant." -#: agenda_culturel/forms.py:357 +#: agenda_culturel/forms.py:359 msgid "Make all versions independent." msgstr "Rendre toutes les versions indépendantes." -#: agenda_culturel/forms.py:416 +#: agenda_culturel/forms.py:418 msgid "Value of the selected version" msgstr "Valeur de la version sélectionnée" -#: agenda_culturel/forms.py:418 agenda_culturel/forms.py:422 +#: agenda_culturel/forms.py:420 agenda_culturel/forms.py:424 msgid "Value of version {}" msgstr "Valeur de la version {}" -#: agenda_culturel/forms.py:571 +#: agenda_culturel/forms.py:573 msgid "Apply category {} to the event {}" msgstr "Appliquer la catégorie {} à l'événement {}" -#: agenda_culturel/forms.py:588 agenda_culturel/models.py:431 -#: agenda_culturel/models.py:1840 +#: agenda_culturel/forms.py:590 agenda_culturel/models.py:431 +#: agenda_culturel/models.py:1968 msgid "Place" msgstr "Lieu" -#: agenda_culturel/forms.py:590 +#: agenda_culturel/forms.py:592 msgid "Create a missing place" msgstr "Créer un lieu manquant" -#: agenda_culturel/forms.py:600 +#: agenda_culturel/forms.py:602 msgid "Add \"{}\" to the aliases of the place" msgstr "Ajouter « {} » aux alias du lieu" -#: agenda_culturel/forms.py:629 +#: agenda_culturel/forms.py:631 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 " @@ -263,8 +263,8 @@ msgstr "" #: agenda_culturel/models.py:54 agenda_culturel/models.py:99 #: agenda_culturel/models.py:177 agenda_culturel/models.py:386 -#: agenda_culturel/models.py:403 agenda_culturel/models.py:1555 -#: agenda_culturel/models.py:1629 +#: agenda_culturel/models.py:403 agenda_culturel/models.py:484 +#: agenda_culturel/models.py:1672 agenda_culturel/models.py:1746 msgid "Name" msgstr "Nom" @@ -325,7 +325,7 @@ msgid "Tag name" msgstr "Nom de l'étiquette" #: agenda_culturel/models.py:182 agenda_culturel/models.py:414 -#: agenda_culturel/models.py:560 +#: agenda_culturel/models.py:496 agenda_culturel/models.py:609 msgid "Description" msgstr "Description" @@ -444,73 +444,109 @@ msgstr "" msgid "Places" msgstr "Lieux" -#: agenda_culturel/models.py:485 agenda_culturel/models.py:1670 +#: agenda_culturel/models.py:484 +msgid "Organisation name" +msgstr "Nom de l'organisme" + +#: agenda_culturel/models.py:488 +msgid "Website" +msgstr "Site internet" + +#: agenda_culturel/models.py:489 +msgid "Website of the organisation" +msgstr "Site internet de l'organisme" + +#: agenda_culturel/models.py:497 +msgid "Description of the organisation." +msgstr "Description de l'organisme" + +#: agenda_culturel/models.py:504 +msgid "Principal place" +msgstr "Lieu principal" + +#: agenda_culturel/models.py:505 +msgid "" +"Place mainly associated with this organizer. Mainly used if there is a " +"similarity in the name, to avoid redundant displays." +msgstr "" +"Lieu principalement associé à cet organisateur. Principalement utilisé s'il " +"y a une similarité de nom, pour éviter les affichages redondants." + +#: agenda_culturel/models.py:512 +msgid "Organisation" +msgstr "Organisme" + +#: agenda_culturel/models.py:513 +msgid "Organisations" +msgstr "Organismes" + +#: agenda_culturel/models.py:525 agenda_culturel/models.py:1787 msgid "Published" msgstr "Publié" -#: agenda_culturel/models.py:486 +#: agenda_culturel/models.py:526 msgid "Draft" msgstr "Brouillon" -#: agenda_culturel/models.py:487 +#: agenda_culturel/models.py:527 msgid "Trash" msgstr "Corbeille" -#: agenda_culturel/models.py:498 +#: agenda_culturel/models.py:538 msgid "Title" msgstr "Titre" -#: agenda_culturel/models.py:498 +#: agenda_culturel/models.py:538 msgid "Short title" msgstr "Titre court" -#: agenda_culturel/models.py:502 agenda_culturel/models.py:1756 +#: agenda_culturel/models.py:542 agenda_culturel/models.py:1884 msgid "Status" msgstr "Status" -#: agenda_culturel/models.py:508 +#: agenda_culturel/models.py:548 msgid "Category of the event" msgstr "Catégorie de l'événement" -#: agenda_culturel/models.py:515 +#: agenda_culturel/models.py:555 msgid "Day of the event" msgstr "Date de l'événement" -#: agenda_culturel/models.py:518 agenda_culturel/models.py:519 +#: agenda_culturel/models.py:558 agenda_culturel/models.py:559 msgid "Starting time" msgstr "Heure de début" -#: agenda_culturel/models.py:525 +#: agenda_culturel/models.py:565 msgid "End day of the event" msgstr "Fin de l'événement" -#: agenda_culturel/models.py:527 +#: agenda_culturel/models.py:567 msgid "End day of the event, only required if different from the start day." msgstr "" "Date de fin de l'événement, uniquement nécessaire s'il est différent du " "premier jour de l'événement" -#: agenda_culturel/models.py:533 +#: agenda_culturel/models.py:573 msgid "Final time" msgstr "Heure de fin" -#: agenda_culturel/models.py:537 +#: agenda_culturel/models.py:577 msgid "Recurrence" msgstr "Récurrence" -#: agenda_culturel/models.py:542 agenda_culturel/models.py:1675 +#: agenda_culturel/models.py:582 agenda_culturel/models.py:1792 msgid "Location" msgstr "Localisation" -#: agenda_culturel/models.py:543 +#: agenda_culturel/models.py:583 msgid "Address of the event" msgstr "Adresse de l'événement" -#: agenda_culturel/models.py:549 +#: agenda_culturel/models.py:589 msgid "Location (free form)" msgstr "Localisation (forme libre)" -#: agenda_culturel/models.py:551 +#: agenda_culturel/models.py:591 msgid "" "Address of the event in case its not available in the already known places " "(free form)" @@ -518,199 +554,211 @@ msgstr "" "Addresse d'un événement si elle n'est pas déjà présente dans la liste des " "lieux disponible (forme libre)" -#: agenda_culturel/models.py:561 +#: agenda_culturel/models.py:601 +msgid "Organisers" +msgstr "Organisateurs" + +#: agenda_culturel/models.py:603 +msgid "" +"list of event organisers. Organizers will only be displayed if one of them " +"does not normally use the venue." +msgstr "" +"Liste des organisateurs de l'événements. Les organisateurs seront affichés " +"uniquement si au moins un d'entre eux n'utilise pas habituellement le lieu." + +#: agenda_culturel/models.py:610 msgid "General description of the event" msgstr "Description générale de l'événement" -#: agenda_culturel/models.py:567 +#: agenda_culturel/models.py:616 msgid "Illustration (local image)" msgstr "Illustration (image locale)" -#: agenda_culturel/models.py:568 +#: agenda_culturel/models.py:617 msgid "Illustration image stored in the agenda server" msgstr "Image d'illustration stockée sur le serveur de l'agenda" -#: agenda_culturel/models.py:575 +#: agenda_culturel/models.py:624 msgid "Illustration" msgstr "Illustration" -#: agenda_culturel/models.py:576 +#: agenda_culturel/models.py:625 msgid "URL of the illustration image" msgstr "URL de l'image illustrative" -#: agenda_culturel/models.py:582 +#: agenda_culturel/models.py:631 msgid "Illustration description" msgstr "Description de l'illustration" -#: agenda_culturel/models.py:583 +#: agenda_culturel/models.py:632 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:591 +#: agenda_culturel/models.py:640 msgid "Importation source" msgstr "Source d'importation" -#: agenda_culturel/models.py:592 +#: agenda_culturel/models.py:641 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:598 +#: agenda_culturel/models.py:647 msgid "UUIDs" msgstr "UUIDs" -#: agenda_culturel/models.py:599 +#: agenda_culturel/models.py:648 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:605 +#: agenda_culturel/models.py:654 msgid "URLs" msgstr "URLs" -#: agenda_culturel/models.py:606 +#: agenda_culturel/models.py:655 msgid "List of all the urls where this event can be found." msgstr "Liste de toutes les urls où l'événement peut être trouvé." -#: agenda_culturel/models.py:614 +#: agenda_culturel/models.py:663 msgid "A list of tags that describe the event." msgstr "Une liste d'étiquettes décrivant l'événement" -#: agenda_culturel/models.py:621 +#: agenda_culturel/models.py:670 msgid "Other versions" msgstr "" -#: agenda_culturel/models.py:686 +#: agenda_culturel/models.py:735 msgid "Event" msgstr "Événement" -#: agenda_culturel/models.py:687 +#: agenda_culturel/models.py:736 msgid "Events" msgstr "Événements" -#: agenda_culturel/models.py:1546 +#: agenda_culturel/models.py:1663 msgid "Contact message" msgstr "Message de contact" -#: agenda_culturel/models.py:1547 +#: agenda_culturel/models.py:1664 msgid "Contact messages" msgstr "Messages de contact" -#: agenda_culturel/models.py:1550 +#: agenda_culturel/models.py:1667 msgid "Subject" msgstr "Sujet" -#: agenda_culturel/models.py:1551 +#: agenda_culturel/models.py:1668 msgid "The subject of your message" msgstr "Sujet de votre message" -#: agenda_culturel/models.py:1556 +#: agenda_culturel/models.py:1673 msgid "Your name" msgstr "Votre nom" -#: agenda_culturel/models.py:1562 +#: agenda_culturel/models.py:1679 msgid "Email address" msgstr "Adresse email" -#: agenda_culturel/models.py:1563 +#: agenda_culturel/models.py:1680 msgid "Your email address" msgstr "Votre adresse email" -#: agenda_culturel/models.py:1568 +#: agenda_culturel/models.py:1685 msgid "Message" msgstr "Message" -#: agenda_culturel/models.py:1568 +#: agenda_culturel/models.py:1685 msgid "Your message" msgstr "Votre message" -#: agenda_culturel/models.py:1574 +#: agenda_culturel/models.py:1691 msgid "This message is a spam." msgstr "Ce message est un spam." -#: agenda_culturel/models.py:1581 +#: agenda_culturel/models.py:1698 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:1586 +#: agenda_culturel/models.py:1703 msgid "Comments" msgstr "Commentaires" -#: agenda_culturel/models.py:1587 +#: agenda_culturel/models.py:1704 msgid "Comments on the message from the moderation team" msgstr "Commentaires sur ce message par l'équipe de modération" -#: agenda_culturel/models.py:1599 agenda_culturel/models.py:1736 +#: agenda_culturel/models.py:1716 agenda_culturel/models.py:1864 msgid "Recurrent import" msgstr "Import récurrent" -#: agenda_culturel/models.py:1600 +#: agenda_culturel/models.py:1717 msgid "Recurrent imports" msgstr "Imports récurrents" -#: agenda_culturel/models.py:1604 +#: agenda_culturel/models.py:1721 msgid "ical" msgstr "ical" -#: agenda_culturel/models.py:1605 +#: agenda_culturel/models.py:1722 msgid "ical no busy" msgstr "ical sans busy" -#: agenda_culturel/models.py:1606 +#: agenda_culturel/models.py:1723 msgid "ical no VC" msgstr "ical sans VC" -#: agenda_culturel/models.py:1607 +#: agenda_culturel/models.py:1724 msgid "lacoope.org" msgstr "lacoope.org" -#: agenda_culturel/models.py:1608 +#: agenda_culturel/models.py:1725 msgid "la comédie" msgstr "la comédie" -#: agenda_culturel/models.py:1609 +#: agenda_culturel/models.py:1726 msgid "le fotomat" msgstr "le fotomat" -#: agenda_culturel/models.py:1610 +#: agenda_culturel/models.py:1727 msgid "la puce à l'oreille" msgstr "la puce à loreille" -#: agenda_culturel/models.py:1611 +#: agenda_culturel/models.py:1728 msgid "Plugin wordpress MEC" msgstr "Plugin wordpress MEC" -#: agenda_culturel/models.py:1612 +#: agenda_culturel/models.py:1729 msgid "Événements d'une page FB" msgstr "Événements d'une page FB" -#: agenda_culturel/models.py:1613 +#: agenda_culturel/models.py:1730 msgid "la cour des 3 coquins" msgstr "la cour des 3 coquins" -#: agenda_culturel/models.py:1614 +#: agenda_culturel/models.py:1731 msgid "Arachnée concert" msgstr "Arachnée concert" -#: agenda_culturel/models.py:1617 +#: agenda_culturel/models.py:1734 msgid "simple" msgstr "simple" -#: agenda_culturel/models.py:1618 +#: agenda_culturel/models.py:1735 msgid "Headless Chromium" msgstr "chromium sans interface" -#: agenda_culturel/models.py:1619 +#: agenda_culturel/models.py:1736 msgid "Headless Chromium (pause)" msgstr "chromium sans interface (pause)" -#: agenda_culturel/models.py:1624 +#: agenda_culturel/models.py:1741 msgid "daily" msgstr "chaque jour" -#: agenda_culturel/models.py:1626 +#: agenda_culturel/models.py:1743 msgid "weekly" msgstr "chaque semaine" -#: agenda_culturel/models.py:1631 +#: agenda_culturel/models.py:1748 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." @@ -718,135 +766,143 @@ msgstr "" "Nom de l'import récurrent. Attention à choisir un nom compréhensible, car il " "sera public, et affiché sur la page à propos du site." -#: agenda_culturel/models.py:1638 +#: agenda_culturel/models.py:1755 msgid "Processor" msgstr "Processeur" -#: agenda_culturel/models.py:1641 +#: agenda_culturel/models.py:1758 msgid "Downloader" msgstr "Téléchargeur" -#: agenda_culturel/models.py:1648 +#: agenda_culturel/models.py:1765 msgid "Import recurrence" msgstr "Récurrence d'import" -#: agenda_culturel/models.py:1655 +#: agenda_culturel/models.py:1772 msgid "Source" msgstr "Source" -#: agenda_culturel/models.py:1656 +#: agenda_culturel/models.py:1773 msgid "URL of the source document" msgstr "URL du document source" -#: agenda_culturel/models.py:1660 +#: agenda_culturel/models.py:1777 msgid "Browsable url" msgstr "URL navigable" -#: agenda_culturel/models.py:1662 +#: agenda_culturel/models.py:1779 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:1671 +#: agenda_culturel/models.py:1788 msgid "Status of each imported event (published or draft)" msgstr "Status de chaque événement importé (publié ou brouillon)" -#: agenda_culturel/models.py:1676 +#: agenda_culturel/models.py:1793 msgid "Address for each imported event" msgstr "Adresse de chaque événement importé" -#: agenda_culturel/models.py:1684 +#: agenda_culturel/models.py:1801 +msgid "Organiser" +msgstr "Organisateur" + +#: agenda_culturel/models.py:1802 +msgid "Organiser of each imported event" +msgstr "Organisateur de chaque événement importé" + +#: agenda_culturel/models.py:1812 msgid "Category of each imported event" msgstr "Catégorie de chaque événement importé" -#: agenda_culturel/models.py:1692 +#: agenda_culturel/models.py:1820 msgid "Tags for each imported event" msgstr "Étiquettes de chaque événement importé" -#: agenda_culturel/models.py:1693 +#: agenda_culturel/models.py:1821 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:1722 +#: agenda_culturel/models.py:1850 msgid "Running" msgstr "En cours" -#: agenda_culturel/models.py:1723 +#: agenda_culturel/models.py:1851 msgid "Canceled" msgstr "Annulé" -#: agenda_culturel/models.py:1724 +#: agenda_culturel/models.py:1852 msgid "Success" msgstr "Succès" -#: agenda_culturel/models.py:1725 +#: agenda_culturel/models.py:1853 msgid "Failed" msgstr "Erreur" -#: agenda_culturel/models.py:1728 +#: agenda_culturel/models.py:1856 msgid "Batch importation" msgstr "Importation par lot" -#: agenda_culturel/models.py:1729 +#: agenda_culturel/models.py:1857 msgid "Batch importations" msgstr "Importations par lot" -#: agenda_culturel/models.py:1737 +#: agenda_culturel/models.py:1865 msgid "Reference to the recurrent import processing" msgstr "Référence du processus d'import récurrent" -#: agenda_culturel/models.py:1745 +#: agenda_culturel/models.py:1873 msgid "URL (if not recurrent import)" msgstr "URL (si pas d'import récurrent)" -#: agenda_culturel/models.py:1747 +#: agenda_culturel/models.py:1875 msgid "Source URL if no RecurrentImport is associated." msgstr "URL source si aucun import récurrent n'est associé" -#: agenda_culturel/models.py:1760 +#: agenda_culturel/models.py:1888 msgid "Error message" msgstr "Votre message" -#: agenda_culturel/models.py:1764 +#: agenda_culturel/models.py:1892 msgid "Number of collected events" msgstr "Nombre d'événements collectés" -#: agenda_culturel/models.py:1767 +#: agenda_culturel/models.py:1895 msgid "Number of imported events" msgstr "Nombre d'événements importés" -#: agenda_culturel/models.py:1770 +#: agenda_culturel/models.py:1898 msgid "Number of updated events" msgstr "Nombre d'événements mis à jour" -#: agenda_culturel/models.py:1773 +#: agenda_culturel/models.py:1901 msgid "Number of removed events" msgstr "Nombre d'événements supprimés" -#: agenda_culturel/models.py:1781 +#: agenda_culturel/models.py:1909 msgid "Weight" msgstr "Poids" -#: agenda_culturel/models.py:1782 +#: agenda_culturel/models.py:1910 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:1789 +#: agenda_culturel/models.py:1917 msgid "Category applied to the event" msgstr "Catégorie appliquée à l'événement" -#: agenda_culturel/models.py:1794 +#: agenda_culturel/models.py:1922 msgid "Contained in the title" msgstr "Contenu dans le titre" -#: agenda_culturel/models.py:1795 +#: agenda_culturel/models.py:1923 msgid "Text contained in the event title" msgstr "Texte contenu dans le titre de l'événement" -#: agenda_culturel/models.py:1801 +#: agenda_culturel/models.py:1929 msgid "Exact title extract" msgstr "Extrait exact du titre" -#: agenda_culturel/models.py:1803 +#: agenda_culturel/models.py:1931 msgid "" "If checked, the extract will be searched for in the title using the exact " "form (capitals, accents)." @@ -854,19 +910,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans le titre en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:1809 +#: agenda_culturel/models.py:1937 msgid "Contained in the description" msgstr "Contenu dans la description" -#: agenda_culturel/models.py:1810 +#: agenda_culturel/models.py:1938 msgid "Text contained in the description" msgstr "Texte contenu dans la description" -#: agenda_culturel/models.py:1816 +#: agenda_culturel/models.py:1944 msgid "Exact description extract" msgstr "Extrait exact de description" -#: agenda_culturel/models.py:1818 +#: agenda_culturel/models.py:1946 msgid "" "If checked, the extract will be searched for in the description using the " "exact form (capitals, accents)." @@ -874,19 +930,19 @@ msgstr "" "Si coché, l'extrait sera recherché dans la description en utilisant la forme " "exacte (majuscules, accents)" -#: agenda_culturel/models.py:1824 +#: agenda_culturel/models.py:1952 msgid "Contained in the location" msgstr "Contenu dans la localisation" -#: agenda_culturel/models.py:1825 +#: agenda_culturel/models.py:1953 msgid "Text contained in the event location" msgstr "Texte contenu dans la localisation de l'événement" -#: agenda_culturel/models.py:1831 +#: agenda_culturel/models.py:1959 msgid "Exact location extract" msgstr "Extrait exact de localisation" -#: agenda_culturel/models.py:1833 +#: agenda_culturel/models.py:1961 msgid "" "If checked, the extract will be searched for in the location using the exact " "form (capitals, accents)." @@ -894,15 +950,15 @@ msgstr "" "Si coché, l'extrait sera recherché dans la localisation en utilisant la " "forme exacte (majuscules, accents)" -#: agenda_culturel/models.py:1841 +#: agenda_culturel/models.py:1969 msgid "Location from place" msgstr "Localisation depuis le lieu" -#: agenda_culturel/models.py:1850 +#: agenda_culturel/models.py:1978 msgid "Categorisation rule" msgstr "Règle de catégorisation" -#: agenda_culturel/models.py:1851 +#: agenda_culturel/models.py:1979 msgid "Categorisation rules" msgstr "Règles de catégorisation" @@ -910,27 +966,27 @@ msgstr "Règles de catégorisation" msgid "French" msgstr "français" -#: agenda_culturel/views.py:144 +#: agenda_culturel/views.py:145 msgid "Recurrent import name" msgstr "Nome de l'import récurrent" -#: agenda_culturel/views.py:145 +#: agenda_culturel/views.py:146 msgid "Add another" msgstr "Ajouter un autre" -#: agenda_culturel/views.py:146 +#: agenda_culturel/views.py:147 msgid "Browse..." msgstr "Naviguer..." -#: agenda_culturel/views.py:147 +#: agenda_culturel/views.py:148 msgid "No file selected." msgstr "Pas de fichier sélectionné." -#: agenda_culturel/views.py:283 +#: agenda_culturel/views.py:284 msgid "The static content has been successfully updated." msgstr "Le contenu statique a été modifié avec succès." -#: agenda_culturel/views.py:291 +#: agenda_culturel/views.py:292 msgid "" "The event cannot be updated because the import process is not available for " "the referenced sources." @@ -938,33 +994,33 @@ msgstr "" "La mise à jour de l'événement n'est pas possible car le processus d'import " "n'est pas disponible pour les sources référencées." -#: agenda_culturel/views.py:294 +#: agenda_culturel/views.py:295 msgid "The event update has been queued and will be completed shortly." msgstr "" "La mise à jour de l'événement a été mise en attente et sera effectuée sous " "peu." -#: agenda_culturel/views.py:304 +#: agenda_culturel/views.py:305 msgid "The event has been successfully modified." msgstr "L'événement a été modifié avec succès." -#: agenda_culturel/views.py:350 +#: agenda_culturel/views.py:351 msgid "The event has been successfully moderated." msgstr "L'événement a été modéré avec succès." -#: agenda_culturel/views.py:438 +#: agenda_culturel/views.py:439 msgid "The event has been successfully deleted." msgstr "L'événement a été supprimé avec succès." -#: agenda_culturel/views.py:470 +#: agenda_culturel/views.py:471 msgid "The status has been successfully modified." msgstr "Le status a été modifié avec succès." -#: agenda_culturel/views.py:504 +#: agenda_culturel/views.py:505 msgid "The event was created: {}." msgstr "L'événement a été créé: {}." -#: agenda_culturel/views.py:506 agenda_culturel/views.py:531 +#: agenda_culturel/views.py:507 agenda_culturel/views.py:532 msgid "" "The event has been submitted and will be published as soon as it has been " "validated by the moderation team." @@ -972,82 +1028,82 @@ msgstr "" "L'événement a été soumis et sera publié dès qu'il aura été validé par " "l'équipe de modération." -#: agenda_culturel/views.py:525 +#: agenda_culturel/views.py:526 msgid "The event is saved." msgstr "L'événement est enregistré." -#: agenda_culturel/views.py:619 agenda_culturel/views.py:671 +#: agenda_culturel/views.py:620 agenda_culturel/views.py:672 msgid "{} has not been submitted since its already known: {}." msgstr "{} n'a pas été soumis car il est déjà connu: {}." -#: agenda_culturel/views.py:624 agenda_culturel/views.py:677 +#: agenda_culturel/views.py:625 agenda_culturel/views.py:678 msgid "" "{} has not been submitted since its already known and currently into " "moderation process." msgstr "{} n'a pas été soumis car il est déjà connu et en cours de modération" -#: agenda_culturel/views.py:634 +#: agenda_culturel/views.py:635 msgid "Integrating {} url(s) into our import process." msgstr "Intégration de {} url(s) dans notre processus d'import." -#: agenda_culturel/views.py:684 +#: agenda_culturel/views.py:685 msgid "Integrating {} into our import process." msgstr "Intégration de {} dans notre processus d'import." -#: agenda_culturel/views.py:739 +#: agenda_culturel/views.py:740 msgid "Your message has been sent successfully." msgstr "Votre message a été envoyé avec succès." -#: agenda_culturel/views.py:749 +#: agenda_culturel/views.py:750 msgid "The contact message has been successfully deleted." msgstr "Le message de contact a été supprimé avec succès." -#: agenda_culturel/views.py:763 +#: agenda_culturel/views.py:764 msgid "The contact message properties has been successfully modified." msgstr "Les propriétés du message de contact ont été modifié avec succès." -#: agenda_culturel/views.py:900 +#: agenda_culturel/views.py:901 msgid "Spam has been successfully deleted." msgstr "Le spam a été supprimé avec succès" -#: agenda_culturel/views.py:1016 +#: agenda_culturel/views.py:1017 msgid "The import has been run successfully." msgstr "L'import a été lancé avec succès" -#: agenda_culturel/views.py:1035 +#: agenda_culturel/views.py:1036 msgid "The import has been canceled." msgstr "L'import a été annulé" -#: agenda_culturel/views.py:1109 +#: agenda_culturel/views.py:1110 msgid "The recurrent import has been successfully modified." msgstr "L'import récurrent a été modifié avec succès." -#: agenda_culturel/views.py:1118 +#: agenda_culturel/views.py:1119 msgid "The recurrent import has been successfully deleted." msgstr "L'import récurrent a été supprimé avec succès" -#: agenda_culturel/views.py:1158 +#: agenda_culturel/views.py:1159 msgid "The import has been launched." msgstr "L'import a été lancé" -#: agenda_culturel/views.py:1180 +#: agenda_culturel/views.py:1181 msgid "Imports has been launched." msgstr "Les imports ont été lancés" -#: agenda_culturel/views.py:1239 +#: agenda_culturel/views.py:1243 msgid "Update successfully completed." msgstr "Mise à jour réalisée avec succès." -#: agenda_culturel/views.py:1297 +#: agenda_culturel/views.py:1301 msgid "Creation of a merged event has been successfully completed." msgstr "Création d'un événement fusionné réalisée avec succès." -#: agenda_culturel/views.py:1333 +#: agenda_culturel/views.py:1337 msgid "Events have been marked as unduplicated." msgstr "Les événements ont été marqués comme non dupliqués." -#: agenda_culturel/views.py:1347 agenda_culturel/views.py:1356 -#: agenda_culturel/views.py:1374 +#: agenda_culturel/views.py:1351 agenda_culturel/views.py:1360 +#: agenda_culturel/views.py:1378 msgid "" "The selected item is no longer included in the list of duplicates. Someone " "else has probably modified the list in the meantime." @@ -1055,23 +1111,23 @@ msgstr "" "L'élément sélectionné ne fait plus partie de la liste des dupliqués. Une " "autre personne a probablement modifié la liste entre temps." -#: agenda_culturel/views.py:1350 +#: agenda_culturel/views.py:1354 msgid "The selected event has been set as representative" msgstr "L'événement sélectionné a été défini comme representatif." -#: agenda_culturel/views.py:1365 +#: agenda_culturel/views.py:1369 msgid "The event has been withdrawn from the group and made independent." msgstr "L'événement a été retiré du groupe et rendu indépendant." -#: agenda_culturel/views.py:1409 +#: agenda_culturel/views.py:1413 msgid "Cleaning up duplicates: {} item(s) fixed." msgstr "Nettoyage des dupliqués: {} élément(s) corrigé(s)." -#: agenda_culturel/views.py:1458 +#: agenda_culturel/views.py:1462 msgid "The event was successfully duplicated." msgstr "L'événement a été marqué dupliqué avec succès." -#: agenda_culturel/views.py:1466 +#: agenda_culturel/views.py:1470 msgid "" "The event has been successfully flagged as a duplicate. The moderation team " "will deal with your suggestion shortly." @@ -1079,32 +1135,32 @@ msgstr "" "L'événement a été signalé comme dupliqué avec succès. Votre suggestion sera " "prochainement prise en charge par l'équipe de modération." -#: agenda_culturel/views.py:1519 +#: agenda_culturel/views.py:1523 msgid "The categorisation rule has been successfully modified." msgstr "La règle de catégorisation a été modifiée avec succès." -#: agenda_culturel/views.py:1528 +#: agenda_culturel/views.py:1532 msgid "The categorisation rule has been successfully deleted." msgstr "La règle de catégorisation a été supprimée avec succès" -#: agenda_culturel/views.py:1550 +#: agenda_culturel/views.py:1554 msgid "The rules were successfully applied and 1 event was categorised." msgstr "" "Les règles ont été appliquées avec succès et 1 événement a été catégorisé" -#: agenda_culturel/views.py:1557 +#: agenda_culturel/views.py:1561 msgid "The rules were successfully applied and {} events were categorised." msgstr "" "Les règles ont été appliquées avec succès et {} événements ont été " "catégorisés" -#: agenda_culturel/views.py:1564 agenda_culturel/views.py:1617 +#: agenda_culturel/views.py:1568 agenda_culturel/views.py:1621 msgid "The rules were successfully applied and no events were categorised." msgstr "" "Les règles ont été appliquées avec succès et aucun événement n'a été " "catégorisé" -#: agenda_culturel/views.py:1603 +#: agenda_culturel/views.py:1607 msgid "" "The rules were successfully applied and 1 event with default category was " "categorised." @@ -1112,7 +1168,7 @@ msgstr "" "Les règles ont été appliquées avec succès et 1 événement avec catégorie par " "défaut a été catégorisé" -#: agenda_culturel/views.py:1610 +#: agenda_culturel/views.py:1614 msgid "" "The rules were successfully applied and {} events with default category were " "categorised." @@ -1120,50 +1176,58 @@ msgstr "" "Les règles ont été appliquées avec succès et {} événements avec catégorie " "par défaut ont été catégorisés" -#: agenda_culturel/views.py:1699 agenda_culturel/views.py:1761 -#: agenda_culturel/views.py:1799 +#: agenda_culturel/views.py:1706 agenda_culturel/views.py:1768 +#: agenda_culturel/views.py:1806 msgid "{} events have been updated." msgstr "{} événements ont été mis à jour." -#: agenda_culturel/views.py:1702 agenda_culturel/views.py:1763 -#: agenda_culturel/views.py:1802 +#: agenda_culturel/views.py:1709 agenda_culturel/views.py:1770 +#: agenda_culturel/views.py:1809 msgid "1 event has been updated." msgstr "1 événement a été mis à jour" -#: agenda_culturel/views.py:1704 agenda_culturel/views.py:1765 -#: agenda_culturel/views.py:1804 +#: agenda_culturel/views.py:1711 agenda_culturel/views.py:1772 +#: agenda_culturel/views.py:1811 msgid "No events have been modified." msgstr "Aucun événement n'a été modifié." -#: agenda_culturel/views.py:1713 +#: agenda_culturel/views.py:1720 msgid "The place has been successfully updated." msgstr "Le lieu a été modifié avec succès." -#: agenda_culturel/views.py:1722 +#: agenda_culturel/views.py:1729 msgid "The place has been successfully created." msgstr "Le lieu a été créé avec succès." -#: agenda_culturel/views.py:1787 +#: agenda_culturel/views.py:1794 msgid "The selected place has been assigned to the event." msgstr "Le lieu sélectionné a été assigné à l'événement." -#: agenda_culturel/views.py:1791 +#: agenda_culturel/views.py:1798 msgid "A new alias has been added to the selected place." msgstr "Un nouvel alias a été créé pour le lieu sélectionné." -#: agenda_culturel/views.py:1847 +#: agenda_culturel/views.py:1891 +msgid "The organisation has been successfully updated." +msgstr "L'organisme a été modifié avec succès." + +#: agenda_culturel/views.py:1900 +msgid "The organisation has been successfully created." +msgstr "L'organisme a été créé avec succès." + +#: agenda_culturel/views.py:1917 msgid "The tag has been successfully updated." msgstr "L'étiquette a été modifiée avec succès." -#: agenda_culturel/views.py:1854 +#: agenda_culturel/views.py:1924 msgid "The tag has been successfully created." msgstr "L'étiquette a été créée avec succès." -#: agenda_culturel/views.py:1918 +#: agenda_culturel/views.py:1988 msgid "You have not modified the tag name." msgstr "Vous n'avez pas modifié le nom de l'étiquette." -#: agenda_culturel/views.py:1928 +#: agenda_culturel/views.py:1998 msgid "" "This tag {} is already in use, and is described by different information " "from the current tag. You can force renaming by checking the corresponding " @@ -1176,7 +1240,7 @@ msgstr "" "sera supprimée, et tous les événements associés à l'étiquette {} seront " "associés à l'étiquette {}." -#: agenda_culturel/views.py:1935 +#: agenda_culturel/views.py:2005 msgid "" "This tag {} is already in use. You can force renaming by checking the " "corresponding option." @@ -1184,10 +1248,10 @@ msgstr "" "Cette étiquette {} est déjà utilisée. Vous pouvez forcer le renommage en " "cochant l'option correspondante." -#: agenda_culturel/views.py:1959 +#: agenda_culturel/views.py:2029 msgid "The tag {} has been successfully renamed to {}." msgstr "L'étiquette {} a été renommée avec succès en {}." -#: agenda_culturel/views.py:1993 +#: agenda_culturel/views.py:2063 msgid "The tag {} has been successfully deleted." msgstr "L'événement {} a été supprimé avec succès." diff --git a/src/agenda_culturel/migrations/0114_organisation_event_organisers_and_more.py b/src/agenda_culturel/migrations/0114_organisation_event_organisers_and_more.py new file mode 100644 index 0000000..5ba1f23 --- /dev/null +++ b/src/agenda_culturel/migrations/0114_organisation_event_organisers_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.9 on 2024-11-22 10:12 + +from django.db import migrations, models +import django.db.models.deletion +import django_ckeditor_5.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0113_remove_tag_category'), + ] + + operations = [ + migrations.CreateModel( + name='Organisation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Organisation name', max_length=512, unique=True, verbose_name='Name')), + ('website', models.URLField(blank=True, help_text='Website of the organisation', max_length=1024, null=True, verbose_name='Website')), + ('description', django_ckeditor_5.fields.CKEditor5Field(blank=True, help_text='Description of the organisation.', null=True, verbose_name='Description')), + ('principal_place', models.ForeignKey(blank=True, help_text='Place mainly associated with this organizer. Mainly used if there is a similarity in the name, to avoid redundant displays.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='agenda_culturel.place', verbose_name='Principal place')), + ], + ), + migrations.AddField( + model_name='event', + name='organisers', + field=models.ManyToManyField(blank=True, help_text='list of event organisers. Organizers will only be displayed if one of them does not normally use the venue.', related_name='organised_events', to='agenda_culturel.organisation', verbose_name='Location (free form)'), + ), + migrations.AddField( + model_name='recurrentimport', + name='defaultOrganiser', + field=models.ForeignKey(blank=True, default=None, help_text='Organiser of each imported event', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='agenda_culturel.organisation', verbose_name='Organiser'), + ), + ] diff --git a/src/agenda_culturel/migrations/0115_alter_organisation_options_alter_event_organisers.py b/src/agenda_culturel/migrations/0115_alter_organisation_options_alter_event_organisers.py new file mode 100644 index 0000000..5a06c84 --- /dev/null +++ b/src/agenda_culturel/migrations/0115_alter_organisation_options_alter_event_organisers.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.9 on 2024-11-22 10:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0114_organisation_event_organisers_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='organisation', + options={'verbose_name': 'Organisation', 'verbose_name_plural': 'Organisations'}, + ), + migrations.AlterField( + model_name='event', + name='organisers', + field=models.ManyToManyField(blank=True, help_text='list of event organisers. Organizers will only be displayed if one of them does not normally use the venue.', related_name='organised_events', to='agenda_culturel.organisation', verbose_name='Organisers'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 74907e8..b5ec8b2 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -479,6 +479,46 @@ class Place(models.Model): tags = [] return tags +class Organisation(models.Model): + name = models.CharField( + verbose_name=_("Name"), help_text=_("Organisation name"), max_length=512, null=False, unique=True + ) + + website = models.URLField( + verbose_name=_("Website"), + help_text=_("Website of the organisation"), + max_length=1024, + blank=True, + null=True, + ) + + description = CKEditor5Field( + verbose_name=_("Description"), + help_text=_("Description of the organisation."), + blank=True, + null=True, + ) + + principal_place = models.ForeignKey( + Place, + verbose_name=_("Principal place"), + help_text=_("Place mainly associated with this organizer. Mainly used if there is a similarity in the name, to avoid redundant displays."), + null=True, + on_delete=models.SET_NULL, + blank=True, + ) + + class Meta: + verbose_name = _("Organisation") + verbose_name_plural = _("Organisations") + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("view_organisation", kwargs={'pk': self.pk}) + + class Event(models.Model): class STATUS(models.TextChoices): @@ -556,6 +596,15 @@ class Event(models.Model): blank=True ) + organisers = models.ManyToManyField(Organisation, + related_name='organised_events', + verbose_name=_("Organisers"), + help_text=_( + "list of event organisers. Organizers will only be displayed if one of them does not normally use the venue." + ), + blank=True + ) + description = models.TextField( verbose_name=_("Description"), help_text=_("General description of the event"), @@ -754,6 +803,19 @@ class Event(models.Model): return self.other_versions.get_local_version() + def get_shown_organisers(self): + if self.organisers.count() == 0: + return None + if self.exact_location is None: + has_significant = True + else: + has_significant = self.organisers.filter(~Q(principal_place=self.exact_location)).count() > 0 + + if has_significant: + return self.organisers.all() + else: + return None + def nb_draft_events(): return Event.objects.filter(status=Event.STATUS.DRAFT).count() @@ -779,6 +841,12 @@ class Event(models.Model): # if the download is ok, then create the corresponding file object self.local_image = File(name=basename, file=open(tmpfile, "rb")) + def add_pending_organisers(self, organisers): + self.pending_organisers = organisers + + def has_pending_organisers(self): + return hasattr(self, "pending_organisers") + def set_skip_duplicate_check(self): self.skip_duplicate_check = True @@ -920,29 +988,38 @@ class Event(models.Model): self.recurrence_dtend = self.recurrence_dtstart def prepare_save(self): + logger.warning('AAA') self.update_modification_dates() + logger.warning('BBB') self.update_recurrence_dtstartend() + logger.warning('CCC') # if the image is defined but not locally downloaded if self.image and not self.local_image: self.download_image() + logger.warning('DDD') # remove "/" from tags if self.tags: self.tags = [t.replace('/', '-') for t in self.tags] + logger.warning('EEE') # in case of importation process if self.is_in_importation_process(): + logger.warning('EE1') # try to detect location if not self.exact_location: for p in Place.objects.all(): if p.match(self): self.exact_location = p break + logger.warning('EE2') # try to detect category if not self.category or self.category.name == Category.default_name: CategorisationRule.apply_rules(self) + logger.warning('EE3') + logger.warning('FFF') def save(self, *args, **kwargs): self.prepare_save() @@ -991,6 +1068,10 @@ class Event(models.Model): e.save() def from_structure(event_structure, import_source=None): + logger.warning("from structure") + # organisers is a manytomany relation thus cannot be initialised before creation of the event + organisers = event_structure.pop('organisers', None) + if "category" in event_structure and event_structure["category"] is not None: try: event_structure["category"] = Category.objects.get( @@ -1066,7 +1147,11 @@ class Event(models.Model): if import_source is not None: event_structure["import_sources"] = [import_source] - return Event(**event_structure) + result = Event(**event_structure) + result.add_pending_organisers(organisers) + + return result + def find_similar_events(self): start_time_test = Q(start_time=self.start_time) @@ -1164,10 +1249,24 @@ class Event(models.Model): def masked(self): return self.other_versions and self.other_versions.representative != self + def get_organisers(self): + if self.pk: + return self.organisers.all() + else: + if self.has_pending_organisers(): + return self.pending_organisers + else: + return [] + + def get_comparison(events, all=True): result = [] for attr in Event.data_fields(all=all, local_img=False, exact_location=False): - values = [getattr(e, attr) for e in events] + if attr == 'organisers': + values = [[str(o) for o in e.get_organisers()] for e in events] + logger.warning("values: " + str(values)) + else: + values = [getattr(e, attr) for e in events] values = ["" if v is None else v for v in values] values = [[] if attr == "tags" and v == "" else v for v in values] # only consider fixed part of Facebook urls @@ -1218,7 +1317,7 @@ class Event(models.Model): elist = list(events) + ([self] if self.pk is not None else []) Event.objects.bulk_update(elist, fields=["other_versions"]) - def data_fields(local_img=True, exact_location=True, all=True): + def data_fields(local_img=True, exact_location=True, all=True, no_m2m=False): result = [] if all: @@ -1237,6 +1336,8 @@ class Event(models.Model): "description", "image", ] + if not no_m2m: + result += ["organisers"] if all and local_img: result += ["local_image"] if all and exact_location: @@ -1263,11 +1364,18 @@ class Event(models.Model): return events[0] def update(self, other, all): + + if other.has_pending_organisers(): + logger.warning("set dans le update") + self.organisers.set(other.pending_organisers) # set attributes - for attr in Event.data_fields(all=all): + for attr in Event.data_fields(all=all, no_m2m=True): + logger.warning('on set l attribut ' + attr + ' sur ' + str(self.pk) + ' avec valeur ' + str(getattr(other, attr))) setattr(self, attr, getattr(other, attr)) + logger.warning('suite') + # adjust modified date if required if other.modified_date and self.modified_date < other.modified_date: self.modified_date = other.modified_date @@ -1281,6 +1389,8 @@ class Event(models.Model): # Limitation: the given events should not be considered similar one to another... def import_events(events, remove_missing_from_source=None): + logger.warning("import_events") + to_import = [] to_update = [] @@ -1290,6 +1400,7 @@ class Event(models.Model): # for each event, check if it's a new one, or a one to be updated for event in events: + logger.warning("event " + str(event)) sdate = date.fromisoformat(event.start_day) if event.end_day: edate = date.fromisoformat(event.end_day) @@ -1304,14 +1415,20 @@ class Event(models.Model): if event.uuids and len(event.uuids) > 0: uuids |= set(event.uuids) + logger.warning("avant " + str(event)) + # imported events should be updated event.set_in_importation_process() + logger.warning("step " + str(event)) event.prepare_save() + logger.warning("neeext " + str(event)) + # check if the event has already be imported (using uuid) same_events = event.find_same_events_by_uuid() if len(same_events) != 0: + logger.warning("same non nuls") # check if one event has been imported and not modified in this list same_imported = Event.find_last_pure_import(same_events) pure = True @@ -1331,14 +1448,15 @@ class Event(models.Model): if same_imported.other_versions.representative != same_imported: same_imported.other_versions.representative = None same_imported.other_versions.save() - + logger.warning('on va y updater') same_imported.update(event, pure) # we only update all tags if it"s a pure import same_imported.set_in_importation_process() same_imported.prepare_save() to_update.append(same_imported) else: # otherwise, the new event possibly a duplication of the remaining others. - + logger.warning("hop trash") + # check if it should be published trash = len([e for e in same_events if e.status != Event.STATUS.TRASH]) == 0 if trash: @@ -1359,14 +1477,24 @@ class Event(models.Model): # import this new event to_import.append(event) + logger.warning("apres boucle") # then import all the new events imported = Event.objects.bulk_create(to_import) + # update organisers (m2m relation) + for i, ti in zip(imported, to_import): + if ti.has_pending_organisers(): + logger.warning("set apres bulk create " + str(i.pk)) + + i.organisers.set(ti.pending_organisers) + nb_updated = Event.objects.bulk_update( to_update, - fields=Event.data_fields() + fields=Event.data_fields(no_m2m=True) + ["imported_date", "modified_date", "uuids", "status"], ) + logger.warning("avant remove") + nb_draft = 0 if remove_missing_from_source is not None and max_date is not None: # events that are missing from the import but in database are turned into drafts @@ -1400,6 +1528,8 @@ class Event(models.Model): nb_draft = Event.objects.bulk_update(to_draft, fields=["status"]) + logger.warning("fin ça fait fin") + return imported, nb_updated, nb_draft def set_current_date(self, date): @@ -1678,6 +1808,17 @@ class RecurrentImport(models.Model): null=True, blank=True, ) + + defaultOrganiser = models.ForeignKey( + Organisation, + verbose_name=_("Organiser"), + help_text=_("Organiser of each imported event"), + default=None, + null=True, + blank=True, + on_delete=models.SET_DEFAULT, + ) + defaultCategory = models.ForeignKey( Category, verbose_name=_("Category"), diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index ee634df..63a32be 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -271,6 +271,12 @@ svg { } } +@media only screen and (min-width: 600px) { + .details-entete { + padding-left: 28%; + } +} + .ephemeris-hour { @extend .ephemeris; padding: 1.5em 0.1em; @@ -1385,7 +1391,7 @@ img.preview { scroll-margin-top: 7em; } -.a-venir, .place, .tag, .tag-descriptions { +.a-venir, .place, .tag, .tag-descriptions, .organisation { article#filters { margin: 2em 0; } diff --git a/src/agenda_culturel/templates/agenda_culturel/event_form.html b/src/agenda_culturel/templates/agenda_culturel/event_form.html index f1d00d6..6d67304 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/event_form.html @@ -122,5 +122,17 @@ Duplication de {% else %} } ); + const organisers = document.querySelector('#id_organisers'); + const choices_organisers = new Choices(organisers, + { + placeholderValue: 'Sélectionner les organisateurs ', + allowHTML: true, + delimiter: ',', + removeItemButton: true, + shouldSort: true, + callbackOnCreateTemplates: () => (show_firstgroup) + }); + + {% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/event_form_moderate.html b/src/agenda_culturel/templates/agenda_culturel/event_form_moderate.html index 33d3378..18c270f 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event_form_moderate.html +++ b/src/agenda_culturel/templates/agenda_culturel/event_form_moderate.html @@ -92,6 +92,18 @@ callbackOnCreateTemplates: () => (show_firstgroup) } ); + const organisers = document.querySelector('#id_organisers'); + const choices_organisers = new Choices(organisers, + { + placeholderValue: 'Sélectionner les organisateurs ', + allowHTML: true, + delimiter: ',', + removeItemButton: true, + shouldSort: true, + callbackOnCreateTemplates: () => (show_firstgroup) + }); + + {% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/navigation.html b/src/agenda_culturel/templates/agenda_culturel/navigation.html index 825df3f..9e7ba7c 100644 --- a/src/agenda_culturel/templates/agenda_culturel/navigation.html +++ b/src/agenda_culturel/templates/agenda_culturel/navigation.html @@ -4,9 +4,11 @@ précédent {% endif %} + {% if page_obj.paginator.num_pages != 1 %} Page {{ page_obj.number }} sur {{ page_obj.paginator.num_pages }} + {% endif %} {% if page_obj.has_next %} suivant diff --git a/src/agenda_culturel/templates/agenda_culturel/organisation_confirm_delete.html b/src/agenda_culturel/templates/agenda_culturel/organisation_confirm_delete.html new file mode 100644 index 0000000..235a479 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/organisation_confirm_delete.html @@ -0,0 +1,25 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}{% block og_title %}Supprimer l'organisateur {{ object.name }}{% endblock %}{% endblock %} + +{% block fluid %}{% endblock %} + +{% block configurer-bouton %}{% endblock %} + +{% block content %} + +
+
+

Supprimer l'organisateur {{ object.name }}

+
+
{% csrf_token %} +

Êtes-vous sûr·e de vouloir supprimer l'organisateur « {{ object.name }} ({{ object.pk }}) » ?

+ {{ form }} +
+ Annuler + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/organisation_detail.html b/src/agenda_culturel/templates/agenda_culturel/organisation_detail.html new file mode 100644 index 0000000..cb52361 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/organisation_detail.html @@ -0,0 +1,84 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}{% block og_title %}{{ object.name }}{% endblock %}{% endblock %} + +{% load tag_extra %} +{% load utils_extra %} +{% load cat_extra %} +{% load static %} +{% load cache %} +{% load i18n %} +{% load l10n %} + +{% block entete_header %} + {% css_categories %} + + + + +{% endblock %} + +{% block fluid %}{% endblock %} + +{% block body-class %}organisation{% endblock %} +{% block content %} + +
+
+ {% picto_from_name "chevron-left" %} Toutes les organisations + {% if perms.agenda_culturel.change_organisation %} + + {% endif %} +

{{ object.name }}

+ {% if object.website or object.principal_place %} + + {% endif %} + + {{ object.description|safe }} +
+ + {% get_current_language as LANGUAGE_CODE %} + {% with cache_timeout=user.is_authenticated|yesno:"30,600" %} + {% cache cache_timeout organisation_list user.is_authenticated object page_obj.number past %} +
+ {% if past %} + Voir les événements à venir + {% else %} + Voir les événements passés + {% endif %} +
+ {% if past %} +

Événements passés

+ {% else %} +

Événements à venir

+ {% endif %} + {% if object_list %} + + {% include "agenda_culturel/navigation.html" with page_obj=page_obj %} + + {% for event in object_list %} + {% include "agenda_culturel/single-event/event-elegant-inc.html" with event=event day=0 no_location=1 %} + {% endfor %} + + {% include "agenda_culturel/navigation.html" with page_obj=page_obj %} + + {% else %} +

Aucun événement

+ {% endif %} + {% endcache %} + {% endwith %} +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/organisation_form.html b/src/agenda_culturel/templates/agenda_culturel/organisation_form.html new file mode 100644 index 0000000..06c5d3e --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/organisation_form.html @@ -0,0 +1,33 @@ +{% extends "agenda_culturel/page-admin.html" %} +{% load static %} + +{% block entete_header %} + + + + +{% endblock %} + +{% block title %}{% block og_title %}{% if object %}Description de {{ object.name }}{% else %}Description d'un organisme{% endif %}{% endblock %}{% endblock %} + +{% block fluid %}{% endblock %} + +{% block content %} + +
+
+

{% if object %}Description de {{ object.name }}{% else %}Description d'un organisme{% endif %}

+
+ +
{% csrf_token %} + {{ form.media }} + {{ form.as_p }} + +
+ Annuler + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/organisation_list.html b/src/agenda_culturel/templates/agenda_culturel/organisation_list.html new file mode 100644 index 0000000..16f3f37 --- /dev/null +++ b/src/agenda_culturel/templates/agenda_culturel/organisation_list.html @@ -0,0 +1,76 @@ +{% extends "agenda_culturel/page.html" %} + +{% block title %}{% block og_title %}Liste des organismes{% endblock %}{% endblock %} + +{% block fluid %}{% endblock %} + +{% load utils_extra %} +{% load cat_extra %} +{% block entete_header %} + {% css_categories %} +{% endblock %} + +{% block sidemenu-bouton %} +
  • {% picto_from_name "chevron-up" %}
  • +
  • {% picto_from_name "chevron-down" %}
  • +{% endblock %} + +{% block content %} + + + {% if object_list %} + {% for organisation in object_list %} +
    +
    + {% if user.is_authenticated %} + + {% endif %} +

    {{ organisation.name }}

    +
    + {% if organisation.website or organisation.principal_place %} + + {% endif %} + + +
    + {% endfor %} + {% else %} +

    Il n'y a aucun organisme défini.

    + {% endif %} + +
    +
    + {% include "agenda_culturel/navigation.html" with page_obj=page_obj %} +
    + +
    + + +{% endblock %} \ No newline at end of file diff --git a/src/agenda_culturel/templates/agenda_culturel/page.html b/src/agenda_culturel/templates/agenda_culturel/page.html index e9167a1..93bfdea 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page.html +++ b/src/agenda_culturel/templates/agenda_culturel/page.html @@ -107,6 +107,9 @@
    {% picto_from_name "map-pin" %} lieux
    +
    + {% picto_from_name "users" %} organisateurs +
    {% picto_from_name "tag" %} étiquettes
    diff --git a/src/agenda_culturel/templates/agenda_culturel/place_detail.html b/src/agenda_culturel/templates/agenda_culturel/place_detail.html index 6118e90..21b3172 100644 --- a/src/agenda_culturel/templates/agenda_culturel/place_detail.html +++ b/src/agenda_culturel/templates/agenda_culturel/place_detail.html @@ -49,10 +49,28 @@ {% endif %} {% endwith %} - {% if not object.description|html_vide %} + + {% if object.description and not object.description|html_vide %}

    Description du lieu

    {{ object.description|safe }} {% endif %} + + {% with object.organisation_set.all as organisations %} + {% if organisations|length == 1 %} +

    L'organisme {{ organisations.0 }} organise régulièrement des événements dans ce lieu.

    + {% endif %} + {% if organisations|length > 1 %} +

    Les organismes suivants utilisent régulièrement ce lieu :

    + + {% endif %} + + {% endwith %} + +
    @@ -78,12 +96,12 @@ Voir les événements passés {% endif %}
    + {% if past %} +

    Événements passés

    + {% else %} +

    Événements à venir

    + {% endif %} {% if object_list %} - {% if past %} -

    Événements passés

    - {% else %} -

    Événements à venir

    - {% endif %} {% include "agenda_culturel/navigation.html" with page_obj=page_obj %} diff --git a/src/agenda_culturel/templates/agenda_culturel/side-nav.html b/src/agenda_culturel/templates/agenda_culturel/side-nav.html index 7ad680a..dc8e4c6 100644 --- a/src/agenda_culturel/templates/agenda_culturel/side-nav.html +++ b/src/agenda_culturel/templates/agenda_culturel/side-nav.html @@ -24,13 +24,16 @@
  • Résumé des activités
  • - {% if perms.agenda_culturel.change_place %} -

    Lieux

    + {% if perms.agenda_culturel.change_place or perms.agenda_culturel.change_organisation %} +

    Lieux et organisateurs