Ajout d'une page événements à venir

This commit is contained in:
Jean-Marie Favreau 2024-10-05 16:23:52 +02:00
parent 670961e6d0
commit 8eaee2b1a6
10 changed files with 576 additions and 215 deletions

View File

@ -2,6 +2,9 @@ from datetime import datetime, timedelta, date, time
import calendar
from django.db.models import Q
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.template.defaultfilters import date as _date
import logging
@ -28,10 +31,12 @@ class DayInCalendar:
self.in_past = d < now
self.today = d == now
self.tomorrow = d == now + timedelta(days=+1)
self.events = []
self.on_requested_interval = on_requested_interval
self.events_by_category = {}
self.time_intervals = None
def is_in_past(self):
return self.in_past
@ -39,6 +44,9 @@ class DayInCalendar:
def is_today(self):
return self.today
def is_tomorrow(self):
return self.tomorrow
def is_ancestor_uuid_event_from_other(self, event):
for e in self.events:
if event.is_ancestor_by_uuid(e):
@ -114,6 +122,51 @@ class DayInCalendar:
result.append((c.name, self.events_by_category[c.name]))
return result
def build_time_intervals(self, all_day_name, interval_names, interval_markers):
self.time_intervals = [IntervalInDay(self.date, i, n) for i, n in enumerate([all_day_name] + interval_names)]
nm2 = datetime.now() + timedelta(hours=-2)
for e in self.events:
if e.start_time is None:
self.time_intervals[0].add_event(e)
else:
dt = datetime.combine(e.start_day, e.start_time)
if dt >= nm2:
ok = False
for i in range(len(interval_markers)):
if dt < interval_markers[i]:
self.time_intervals[i + 1].add_event(e)
ok = True
break
if not ok:
self.time_intervals[-1].add_event(e)
def get_time_intervals(self):
if self.time_intervals is None:
if self.is_today():
all_day_name = _('All day today')
interval_names = [_('This morning'), _('This noon'), _('This afternoon'), _('This evening')]
elif self.is_tomorrow():
name = _("Tomorrow")
all_day_name = _('All day tomorrow')
interval_names = [_('%s morning') % name, _('%s noon') % name, _('%s afternoon') % name, _('%s evening') % name]
else:
name = _date(self.date, "l")
all_day_name = _('All day %s') % name
interval_names = [_('%s morning') % name, _('%s noon') % name, _('%s afternoon') % name, _('%s evening') % name]
interval_markers = [datetime.combine(self.date, time(h, m)) for h, m in [(11, 30), (13, 0), (18, 0)]]
self.build_time_intervals(all_day_name, interval_names, interval_markers)
logger.error("hop " + str(len(self.time_intervals)))
return self.time_intervals
class IntervalInDay(DayInCalendar):
def __init__(self, d, id, name):
self.name = name
self.id = d.strftime('%Y-%m-%d') + '-' + str(id)
super().__init__(d)
class CalendarList:
def __init__(self, firstdate, lastdate, filter=None, exact=False):
@ -208,6 +261,21 @@ class CalendarList:
def calendar_days_list(self):
return list(self.get_calendar_days().values())
def time_intervals_list(self, onlyfirst=False):
ds = self.calendar_days_list()
result = []
for d in ds:
tis = d.get_time_intervals()
for t in tis:
if len(t.events) > 0:
result.append(t)
if onlyfirst:
break
return result
def time_intervals_list_first(self):
return self.time_intervals_list(True)
def export_to_ics(self):
from .models import Event
events = [event for day in self.get_calendar_days().values() for event in day.events]

File diff suppressed because it is too large Load Diff

View File

@ -253,7 +253,7 @@ svg {
.illustration {
width: 100%;
padding: 0.3em;
margin: 0;
margin: 0 0 0.5em 0;
}
@media only screen and (min-width: 550px) {
@ -526,6 +526,12 @@ article#filters {
.slide-buttons {
float: right;
margin-bottom: -2.8em;
}
@media only screen and (min-width: 992px) {
.slide-buttons {
margin-left: -3em;
}
}
.highlight {
@ -1170,6 +1176,7 @@ article {
}
#boutons-fixes {
z-index: 1000;
li {
list-style: none;
@extend [role="button"], .secondary;
@ -1201,4 +1208,12 @@ article {
display: inline-block;
}
}
.a-venir {
.day-interval>.sticky {
position: sticky;
top: 0;
z-index: 10;
}
}

View File

@ -0,0 +1,95 @@
{% extends "agenda_culturel/page.html" %}
{% load i18n %}
{% load cache %}
{% load cat_extra %}
{% load event_extra %}
{% load utils_extra %}
{% load static %}
{% load i18n %}
{% block entete_header %}
{% css_categories %}
<script src="{% static 'js/modal.js' %}"></script>
<script src="{% static 'js/filters.js' %}"></script>
{% endblock %}
{% block title %}{% block og_title %}Événements à venir{% endblock %}{% endblock %}
{% block body-class %}a-venir{% endblock %}
{% block content %}
{% get_current_language as LANGUAGE_CODE %}
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
{% cache cache_timeout upcoming user.is_authenticated calendar.firstdate filter.to_str LANGUAGE_CODE %}
<article>
<header><h1 id="index-avenir">Événements à venir</h1></header>
{% include "agenda_culturel/filter-inc.html" with filter=filter %}
<footer>
{% for ti in calendar.time_intervals_list_first %}
<a href="#{{ ti.id }}" role="button">
{% if ti.is_today %}Aujourd'hui
{% else %}
{% if ti.is_tomorrow %}Demain
{% else %}
{{ ti.date| date:"l j" }}
{% endif %}
{% endif %}
{% picto_from_name "chevrons-down" %}</a>
{% endfor %}
</footer>
</article>
{% for ti in calendar.time_intervals_list %}
<article class="day-interval" id="{{ ti.id }}">
<header class="sticky">
<a role="button" class="secondary slide-buttons" href="#index-avenir" data-placement="left" data-tooltip="Retour en haut">{% picto_from_name "chevrons-up" %}</a>
<hgroup>
<h2>{{ ti.name }}</h2>
<h3>{{ ti.date }}</h3>
</hgroup>
</header>
<div class="grid two-columns grid-reverse">
<div>
{% if ti.events|length > 1 %}
<h3>Résumé</h3>
<ul>
{% for event in ti.events %}
<li>{{ event.category | circle_cat:event.has_recurrences }}
{% if event.start_time %}
{{ event.start_time }}
{% endif %}
{{ event|picto_status }} <a href="#event-{{ event.id }}">{{ event.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div>
{% if ti.events|length > 1 %}
<h3>Événements</h3>
{% endif %}
{% with indexti=ti.id %}
{% for event in ti.events %}
{% include "agenda_culturel/single-event/event-in-upcoming-inc.html" with event=event filter=filter day=day indexti=indexti %}
{% endfor %}
{% endwith %}
</div>
</div>
</article>
{% endfor %}
{% endcache %}
{% endwith %}
{% endblock %}

View File

@ -33,7 +33,7 @@
{% load utils_extra %}
{% load duplicated_extra %}
{% load rimports_extra %}
<body>
<body class="{% block body-class %}contenu{% endblock %}">
<div id="boutons-fixes">
<ul>
{% block ajouter-bouton %}<li class="ajouter-bouton"><a href="{% url 'add_event' %}" aria-label="Ajouter un événement">{% picto_from_name "plus" %}</a></li>{% endblock %}
@ -47,7 +47,7 @@
<ul class="menu">
{% block ajouter-menu %}<li id="menu-ajouter" class="ajouter-bouton"><a href="{% url 'add_event' %}">Ajouter un événement {% picto_from_name "plus-circle" %}</a></li>{% endblock %}
{% block rechercher-menu %}<li id="menu-rechercher" class="rechercher-bouton"><a href="{% url 'event_search' %}">Rechercher {% picto_from_name "search" %}</a></li>{% endblock %}
<li><a href="{% url 'aujourdhui' %}">Aujourd'hui</a></li>
<li><a href="{% url 'a_venir' %}">À venir</a></li>
<li><a href="{% url 'cette_semaine' %}">Cette semaine</a></li>
<li><a href="{% url 'ce_mois_ci' %}">Ce mois-ci</a></li>
</ul>

View File

@ -68,7 +68,7 @@
{% endfor %}
</p>
{% else %}
<p><em>Cet événement est disponible uniquement sur les nuits énimagmatiques.</em></p>
<p><em>À notre connaissance, cet événement n'est pas référencé autre part sur internet.</em></p>
{% endif %}
{% if event.has_recurrences %}
<p class="footer">

View File

@ -0,0 +1,83 @@
{% load static %}
{% load cat_extra %}
{% load utils_extra %}
{% load event_extra %}
{% load tag_extra %}
<article id="event-{{ event.pk}}">
<a role="button" class="secondary slide-buttons" href="#{{ indexti }}" data-tooltip="Retour au résumé" data-placement="left">{% picto_from_name "arrow-up" %}</a>
{% if event.image or event.local_image %}
<article class='illustration'>
<img src="{% if event.local_image %}{{ event.local_image.url }}{% else %}{{ event.image }}{% endif %}" alt="{{ event.image_alt }}" />
</article>
{% endif %}
{% if event|can_show_start_time:day %}
{% if event.start_time %}
<article class='ephemeris-hour'>
<span class="large">{{ event.start_time }}</span>
</article>
{% endif %}
{% endif %}
{% if event|can_show_end_time:day %}
{% if event.end_time %}
<article class='ephemeris-hour'>
jusqu'à <span class="large">{{ event.end_time }}</span>
</article>
{% endif %}
{% endif %}
{{ event.category | small_cat_recurrent:event.has_recurrences }}
{% if event.location or event.exact_location %}<hgroup>{% endif %}
<h3>
{{ event|picto_status }}
<a href="{{ event.get_absolute_url }}">{{ event.title }}</a>
</h3>
{% if event.location or event.exact_location %}
<h4>
{% picto_from_name "map-pin" %}
{% include "agenda_culturel/event-location-inc.html" with event=event %}
</h4>
</hgroup>
{% endif %}
{% if event|need_complete_display:True %}<p>
{% picto_from_name "calendar" %}
<em>{% if event.end_day and event.end_day != event.start_day %}Cet événement dure du {% else %}Cet événement a lieu le{% endif %}
{% include "agenda_culturel/date-times-inc.html" with event=event %}
</em></p>
{% endif %}
<p>{{ event.description |linebreaks2 | truncatewords:60 }}</p>
<footer class="infos-and-buttons">
<div class="infos">
<p>
{% for tag in event.tags %}
<a href="{% url 'view_tag' tag %}" role="button" class="small-cat">{{ tag }}</a>
{% endfor %}
</p>
{% if event.has_recurrences %}
<p class="footer">
{% picto_from_name "repeat" %}
<!-- TODO: see https://forge.chapril.org/jmtrivial/agenda_culturel/issues/65 -->
{% for r in event.recurrences.rrules %}
{{ r.to_text }}{% if not forloop.first %}, {% endif %}{% endfor %}, depuis le {{ event.recurrences.dtstart.date }}
</p>
{% endif %}
</div>
{% if perms.agenda_culturel.change_event %}
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}
<a href="{{ event.get_absolute_url }}" role="button">Voir l'événement <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<use href="{% static 'images/feather-sprite.svg' %}#chevron-right" />
</svg></a>
</div>
{% endif %}
</footer>
</article>

View File

@ -2,7 +2,7 @@ from django import template
from django.utils.safestring import mark_safe
from django.urls import reverse_lazy
from django.template.defaultfilters import pluralize, linebreaks, urlize
import re
from agenda_culturel.models import Event
from django.db.models import Q
@ -139,3 +139,9 @@ def add_url_category(url, c):
@register.filter
def robust_urlize(txt):
return mark_safe(urlize(mark_safe(txt.replace("http", " http"))).replace(" <a ", "<a "))
@register.filter
def linebreaks2(txt):
res = linebreaks(txt)
return mark_safe(re.sub("(<br> )+", "<br>", res))

View File

@ -14,6 +14,7 @@ urlpatterns = [
path("mois/<int:year>/<int:month>/", month_view, name="month_view"),
path("jour/<int:year>/<int:month>/<int:day>/", day_view, name="day_view"),
path("aujourdhui/", day_view, name="aujourdhui"),
path("a-venir/", upcoming_events, name="a_venir"),
path("cette-semaine/", week_view, name="cette_semaine"),
path("ce-mois-ci", month_view, name="ce_mois_ci"),
path("tag/<t>/", view_tag, name="view_tag"),

View File

@ -473,6 +473,28 @@ def day_view(request, year=None, month=None, day=None):
return render(request, "agenda_culturel/page-day.html", context)
def upcoming_events(request, year=None, month=None, day=None):
now = date.today()
if year is None:
year = now.year
if month is None:
month = now.month
if day is None:
day = now.day
day = date(year, month, day)
request = EventFilter.set_default_values(request)
filter = EventFilter(request.GET, get_event_qs(request), request=request)
cal = CalendarList(now, now + timedelta(days=4), filter, True)
context = {
"calendar": cal,
"now": now,
"filter": filter,
}
return render(request, "agenda_culturel/page-upcoming.html", context)
def view_tag(request, t):
events = Event.objects.filter(tags__contains=[t]).order_by(
"start_day", "start_time"