diff --git a/experimentations/get_facebook_event.py b/experimentations/get_facebook_event.py index e90be7f..0f16c7e 100755 --- a/experimentations/get_facebook_event.py +++ b/experimentations/get_facebook_event.py @@ -30,7 +30,7 @@ from src.agenda_culturel.import_tasks.extractor_facebook import * if __name__ == "__main__": u2e = URL2Events(ChromiumHeadlessDownloader(), FacebookEventExtractor()) - url="https://www.facebook.com/events/s/tour-du-sud-invite-koum/430014373384441/" + url="https://www.facebook.com/events/3575802569389184/3575802576055850/?active_tab=about" events = u2e.process(url, cache = "fb.html", published = True) diff --git a/src/agenda_culturel/filters.py b/src/agenda_culturel/filters.py index 31ad8ba..93dd88d 100644 --- a/src/agenda_culturel/filters.py +++ b/src/agenda_culturel/filters.py @@ -4,7 +4,7 @@ from django import forms from django.contrib.postgres.search import SearchQuery, SearchHeadline from django.db.models import Count, Q from datetime import date, timedelta - +from urllib.parse import urlparse, parse_qs, urlencode from django.http import QueryDict from django.contrib.gis.measure import D @@ -98,15 +98,6 @@ class EventFilter(django_filters.FilterSet): method="filter_recurrences", ) - category = django_filters.ModelMultipleChoiceFilter( - label="Filtrer par catégories", - field_name="category__id", - to_field_name="id", - queryset=Category.objects.all(), - widget=MultipleHiddenInput, - ) - - status = django_filters.MultipleChoiceFilter( label="Filtrer par status", choices=Event.STATUS.choices, @@ -116,7 +107,7 @@ class EventFilter(django_filters.FilterSet): class Meta: model = Event - fields = ["category", "tags", "exclude_tags", "status", "recurrences"] + fields = ["tags", "exclude_tags", "status", "recurrences"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -125,6 +116,28 @@ class EventFilter(django_filters.FilterSet): self.form.fields["exclude_tags"].choices = Tag.get_tag_groups(exclude=True, nb_suggestions=0) self.form.fields["tags"].choices = Tag.get_tag_groups(include=True) + def has_category_parameters(self): + url = self.request.get_full_path() + return "category=" in url and not url.startswith("/cat:") + + def get_new_url(self): + url = self.request.get_full_path() + if url.startswith("/cat:"): + return url + else: + parsed_url = urlparse(url) + params = parse_qs(parsed_url.query) + if len(params['category']) == 0: + return url + else: + category = Category.objects.filter(pk=params['category'][0]).first() + del params["category"] + url = parsed_url._replace(query=urlencode(params, doseq=True)).geturl() + if category is None: + return url + else: + return "/cat:" + category.slug + url + def filter_recurrences(self, queryset, name, value): # construct the full lookup expression lookup = "__".join([name, "isnull"]) @@ -153,6 +166,20 @@ class EventFilter(django_filters.FilterSet): return parent.exclude(exact_location=False).filter(exact_location__location__distance_lt=(p, D(km=d))) + def has_location(self): + d = self.get_cleaned_data("radius") + p = self.get_cleaned_data("position") + if not isinstance(d, str) or not isinstance(p, ReferenceLocation): + return False + try: + d = float(d) + except ValueError: + return False + if d <= 0: + return False + + return True + def get_url(self): if isinstance(self.form.data, QueryDict): return self.form.data.urlencode() @@ -162,43 +189,8 @@ class EventFilter(django_filters.FilterSet): def get_full_url(self): return self.request.get_full_path() - def get_url_remove_categories(self, catpks, full_path = None): - if full_path is None: - full_path = self.request.get_full_path() - - result = full_path - for catpk in catpks: - result = result.replace('category=' + str(catpk), '') - result = result.replace('?&', '?') - result = result.replace('&&', '&') - return result - - def get_url_add_categories(self, catpks, full_path = None): - if full_path is None: - full_path = self.request.get_full_path() - - result = full_path - for catpk in catpks: - result = result + ('&' if '?' in full_path else '?') + 'category=' + str(catpk) - return result - - def get_url_without_filters_only_cats(self): - return self.get_url_without_filters(True) - - - def get_url_without_filters(self, only_categories=False): - - if only_categories: - # on repart d'une url sans option - result = self.request.get_full_path().split("?")[0] - # on ajoute toutes les catégories - result = self.get_url_add_categories([c.pk for c in self.get_categories()], result) - else: - # on supprime toutes les catégories - result = self.get_url_remove_categories([c.pk for c in self.get_categories()]) - - return result - + def get_url_without_filters(self): + return self.request.get_full_path().split("?")[0] def get_cleaned_data(self, name): @@ -209,12 +201,6 @@ class EventFilter(django_filters.FilterSet): except KeyError: return {} - def get_categories(self): - return self.get_cleaned_data("category") - - def has_category(self): - return "category" in self.form.cleaned_data and len(self.get_cleaned_data("category")) > 0 - def get_tags(self): return self.get_cleaned_data("tags") @@ -232,7 +218,7 @@ class EventFilter(django_filters.FilterSet): def to_str(self, prefix=''): self.form.full_clean() - result = ' '.join([c.name for c in self.get_categories()] + [t for t in self.get_tags()] + ["~" + t for t in self.get_exclude_tags()] + [str(self.get_position()), str(self.get_radius())]) + result = ' '.join([t for t in self.get_tags()] + ["~" + t for t in self.get_exclude_tags()] + [str(self.get_position()), str(self.get_radius())]) if len(result) > 0: result = prefix + result return result @@ -256,29 +242,25 @@ class EventFilter(django_filters.FilterSet): else: return "" - - def is_resetable(self, only_categories=False): - if only_categories: - return len(self.get_cleaned_data("category")) != 0 + def is_resetable(self): + if self.request.user.is_authenticated: + if ( + len(self.get_cleaned_data("status")) != 1 + or + self.get_cleaned_data("status")[0] != Event.STATUS.PUBLISHED + ): + return True else: - if self.request.user.is_authenticated: - if ( - len(self.get_cleaned_data("status")) != 1 - or - self.get_cleaned_data("status")[0] != Event.STATUS.PUBLISHED - ): - return True - else: - if ( - len(self.get_cleaned_data("status")) != 0 - ): - return True - return ( - len(self.get_cleaned_data("tags")) != 0 - or len(self.get_cleaned_data("exclude_tags")) != 0 - or len(self.get_cleaned_data("recurrences")) != 0 - or ((not self.get_cleaned_data("position") is None) and (not self.get_cleaned_data("radius") is None)) - ) + if ( + len(self.get_cleaned_data("status")) != 0 + ): + return True + return ( + len(self.get_cleaned_data("tags")) != 0 + or len(self.get_cleaned_data("exclude_tags")) != 0 + or len(self.get_cleaned_data("recurrences")) != 0 + or ((not self.get_cleaned_data("position") is None) and (not self.get_cleaned_data("radius") is None)) + ) def is_active(self, only_categories=False): if only_categories: @@ -292,9 +274,6 @@ class EventFilter(django_filters.FilterSet): or ((not self.get_cleaned_data("position") is None) and (not self.get_cleaned_data("radius") is None)) ) - def is_selected(self, cat): - return "category" in self.form.cleaned_data and cat in self.form.cleaned_data["category"] - def is_selected_tag(self, tag): return "tags" in self.form.cleaned_data and tag in self.form.cleaned_data["tags"] diff --git a/src/agenda_culturel/migrations/0137_category_slug.py b/src/agenda_culturel/migrations/0137_category_slug.py new file mode 100644 index 0000000..46a2b55 --- /dev/null +++ b/src/agenda_culturel/migrations/0137_category_slug.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.9 on 2025-01-24 17:48 + +import autoslug.fields +from django.db import migrations + +def migrate_data_forward(apps, schema_editor): + Category = apps.get_model("agenda_culturel", "Category") + + for instance in Category.objects.all(): + print("Generating slug for %s"%instance) + instance.save() # Will trigger slug update + +def migrate_data_backward(apps, schema_editor): + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0136_alter_recurrentimport_processor'), + ] + + operations = [ + migrations.AddField( + model_name='category', + name='slug', + field=autoslug.fields.AutoSlugField(default=None, editable=False, null=True, populate_from='name', unique=True), + ), + migrations.RunPython( + migrate_data_forward, + migrate_data_backward, + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index 8e9fa0c..2016802 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -17,7 +17,7 @@ from django.core.files.storage import default_storage from django.contrib.sites.models import Site from django.core.mail import send_mail from django.template.loader import render_to_string - +from autoslug import AutoSlugField import uuid @@ -114,6 +114,8 @@ class Category(models.Model): verbose_name=_("Name"), help_text=_("Category name"), max_length=512 ) + slug = AutoSlugField(null=True, default=None, unique=True, populate_from='name') + color = ColorField( verbose_name=_("Color"), help_text=_("Color used as background for the category"), @@ -176,7 +178,7 @@ class Category(models.Model): return "cat-" + str(self.id) def get_absolute_url(self): - return reverse('home') + '?catgory=' + str(self.pk) + return reverse('home_category', kwargs={"cat": self.slug}) def __str__(self): return self.name diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index f189cef..d867255 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -139,7 +139,7 @@ details[role="list"] summary + ul li.selected>a:hover { padding: 0.1em .2em; .titre { vertical-align: middle; - margin-right: .4em; + margin-right: .2em; } display: inline-block; margin: .1em 0; @@ -148,6 +148,12 @@ details[role="list"] summary + ul li.selected>a:hover { background: none; padding: 0.1em 0; } + .developpe { + svg { + margin-right: .5em; + vertical-align: middle; + } + } } .suggestions { diff --git a/src/agenda_culturel/templates/agenda_culturel/filter-inc.html b/src/agenda_culturel/templates/agenda_culturel/filter-inc.html index d6ca6a7..ccac552 100644 --- a/src/agenda_culturel/templates/agenda_culturel/filter-inc.html +++ b/src/agenda_culturel/templates/agenda_culturel/filter-inc.html @@ -9,7 +9,7 @@ {% endif %}
- {% show_legend filter=filter %} + {% show_legend category=category filter=filter %}
@@ -34,7 +34,7 @@ {% endif %} {% if filter.is_resetable %} - {% picto_from_name "x-circle" %} + {% picto_from_name "x-circle" %} {% endif %} diff --git a/src/agenda_culturel/templates/agenda_culturel/page-month.html b/src/agenda_culturel/templates/agenda_culturel/page-month.html index 1d52c97..891c464 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-month.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-month.html @@ -14,10 +14,13 @@ {% endblock %} {% block title %}{% block og_title %} +{% if category %}{{ category.name }} en{% endif %} {{ calendar.firstdate | date:"F o" }} {% endblock %}{% endblock %} -{% block ce_mois_ci_parameters %}{% block cette_semaine_parameters %}{% block a_venir_parameters %}?{{ filter.get_url }}{% endblock %}{% endblock %}{% endblock %} +{% block navigation-menu %} +{% navigation_links filter category %} +{% endblock %} {% block content %} @@ -26,16 +29,20 @@ {% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=0 %} {% with cache_timeout=user.is_authenticated|yesno:"30,600" %} - {% cache cache_timeout month user.is_authenticated calendar.firstdate filter.get_url %} + {% cache cache_timeout month user.is_authenticated calendar.firstdate category filter.get_url %}
-

{{ calendar.firstdate | date:"F o" }}

+

{% if category %}{{ category.name }} en {% endif %}{{ calendar.firstdate | date:"F o" }}{% if filter.has_location %} à {{ filter.get_position }}{% endif %}

{% if day.events %}
{% endfor %} - + {% endif %} @@ -134,19 +153,31 @@
{% if calendar.lastdate|shift_day:+1|not_after_last %} {% if calendar.lastdate|not_before_first %} + {% if category %} + + {% else %} {% endif %} {% endif %} + {% endif %}
{% endcache %} diff --git a/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html b/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html index 3ae0bf8..2348b9e 100644 --- a/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html +++ b/src/agenda_culturel/templates/agenda_culturel/page-upcoming.html @@ -13,7 +13,9 @@ {% endblock %} -{% block ce_mois_ci_parameters %}{% block cette_semaine_parameters %}{% block a_venir_parameters %}?{{ filter.get_url }}{% endblock %}{% endblock %}{% endblock %} +{% block navigation-menu %} +{% navigation_links filter category %} +{% endblock %} {% block body-class %}a-venir{% endblock %} @@ -23,25 +25,30 @@

{% block title %}{% block og_title %} + {% if category %}{{ category.name }}{% else %}Événements{% endif %} {% if calendar.calendar_days_list.0.is_today %} - Événements à venir + à venir {% else %} - Événements du {{ calendar.calendar_days_list.0.date| date:"l j F Y" }} + du {{ calendar.calendar_days_list.0.date| date:"l j F Y" }} {% if calendar.calendar_days_list|length > 1 %} et suivants {% endif %} - {% endif %} + {% endif %}{% if filter.has_location %} à {{ filter.get_position }}{% endif %} {% endblock %}{% endblock %}

{% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=1 %} {% with cache_timeout=user.is_authenticated|yesno:"30,600" %} - {% cache cache_timeout upcoming user.is_authenticated calendar.firstdate filter.get_url calendar.calendar_days_list|length %} + {% cache cache_timeout upcoming user.is_authenticated calendar.firstdate filter.get_url category calendar.calendar_days_list|length %}