mirror of
https://github.com/ae-utbm/sith.git
synced 2026-10-25 11:49:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7e1cdafde | ||
|
|
93b6238c75 | ||
|
|
91865fe7a1 | ||
|
|
1e189f7b2b |
No files matched your search
@@ -0,0 +1,79 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
{% if club.has_rights_in_club(user) %}
|
||||
<a class="btn btn-blue" href="{{ url("com:news_new") }}">
|
||||
<i class="fa fa-plus"></i> {% trans %}Create news{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% for semester_code, semester_events in events %}
|
||||
<h3>{{ semester_code }}</h3>
|
||||
{% for event in semester_events %}
|
||||
<details class="accordion">
|
||||
<summary>{{ event.title }}</summary>
|
||||
<div class="accordion-content">
|
||||
|
||||
{# Events that occur weekly from the start to the end of the semester
|
||||
would take a lot of space if shown by default.
|
||||
Thus, if the event has more than one date, those are
|
||||
hidden by a details element.
|
||||
If there is only one date, it is directly displayed. #}
|
||||
{% set dates = event.dates.all()|list %}
|
||||
{% if dates|length == 1 %}
|
||||
<em>
|
||||
<time datetime="{{ dates[0].start_date.isoformat(timespec="seconds") }}">
|
||||
{{ dates[0].start_date|localtime|date(DATETIME_FORMAT) }},
|
||||
{{ dates[0].start_date|localtime|time(DATETIME_FORMAT) }}
|
||||
</time> -
|
||||
<time datetime="{{ dates[0].end_date.isoformat(timespec="seconds") }}">
|
||||
{{ dates[0].end_date|localtime|date(DATETIME_FORMAT) }},
|
||||
{{ dates[0].end_date|localtime|time(DATETIME_FORMAT) }}
|
||||
</time>
|
||||
</em>
|
||||
{% else %}
|
||||
<details>
|
||||
<summary>
|
||||
{% trans trimmed nb_occurrences=dates|length %}
|
||||
Dates ({{ nb_occurrences }} occurrences)
|
||||
{% endtrans %}
|
||||
</summary>
|
||||
<ul>
|
||||
{% for d in dates %}
|
||||
<li>
|
||||
<time datetime="{{ d.start_date.isoformat(timespec="seconds") }}">
|
||||
{{ d.start_date|localtime|date(DATETIME_FORMAT) }},
|
||||
{{ d.start_date|localtime|time(DATETIME_FORMAT) }}
|
||||
</time> -
|
||||
<time datetime="{{ d.end_date.isoformat(timespec="seconds") }}">
|
||||
{{ d.end_date|localtime|date(DATETIME_FORMAT) }},
|
||||
{{ d.end_date|localtime|time(DATETIME_FORMAT) }}
|
||||
</time>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
<h4>{% trans %}Permalink{% endtrans %}</h4>
|
||||
<p>{{ request.build_absolute_uri(event.get_absolute_url())|urlize }}</p>
|
||||
|
||||
<h4>{% trans %}Summary{% endtrans %}</h4>
|
||||
{% if event.summary %}
|
||||
{{ event.summary|markdown }}
|
||||
{% else %}
|
||||
<p><em>{% trans %}No summary{% endtrans %}</em></p>
|
||||
{% endif %}
|
||||
|
||||
<h4>{% trans %}Description{% endtrans %}</h4>
|
||||
{% if event.content %}
|
||||
{{ event.content|markdown }}
|
||||
{% else %}
|
||||
<p><em>{% trans %}No detailed description{% endtrans %}</em></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
{% endfor %}
|
||||
<br />
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import get_current_timezone
|
||||
from model_bakery import baker
|
||||
|
||||
from club.models import Club
|
||||
from club.views import ClubNewsView
|
||||
from com.models import News, NewsDate
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import User
|
||||
|
||||
|
||||
class TestClubNews(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.club = baker.make(Club)
|
||||
cls.news = baker.make(
|
||||
News, club=cls.club, is_published=True, author=baker.make(User), _quantity=5
|
||||
)
|
||||
d = datetime(year=2025, month=10, day=1, tzinfo=get_current_timezone())
|
||||
dates = [
|
||||
baker.prepare(NewsDate, news=cls.news[0], start_date=d),
|
||||
baker.prepare(NewsDate, news=cls.news[0], start_date=d.replace(day=8)),
|
||||
baker.prepare(NewsDate, news=cls.news[0], start_date=d.replace(day=15)),
|
||||
baker.prepare(NewsDate, news=cls.news[1], start_date=d.replace(month=5)),
|
||||
baker.prepare(NewsDate, news=cls.news[2], start_date=d.replace(month=4)),
|
||||
baker.prepare(NewsDate, news=cls.news[3], start_date=d.replace(year=2024)),
|
||||
baker.prepare(NewsDate, news=cls.news[4], start_date=d.replace(year=2020)),
|
||||
]
|
||||
NewsDate.objects.bulk_create(dates)
|
||||
cls.url = reverse("club:news_list", kwargs={"club_id": cls.club.id})
|
||||
|
||||
def test_load_page(self):
|
||||
"""Just test that the page loads."""
|
||||
self.client.force_login(subscriber_user.make())
|
||||
res = self.client.get(self.url)
|
||||
assert res.status_code == 200
|
||||
|
||||
def test_page_data(self):
|
||||
"""Test that we display the good events."""
|
||||
# For this test, the object we want to test
|
||||
# is built with `itertools.groupby`.
|
||||
# However, it is consumed when rendering the template,
|
||||
# making it unusable for later testing purposes,
|
||||
# so we must use lower level django APIs in this test.
|
||||
request = RequestFactory().get(self.url)
|
||||
request.user = subscriber_user.make()
|
||||
response = ClubNewsView.as_view()(request, club_id=self.club.id)
|
||||
events = [
|
||||
(semester_code, list(dates))
|
||||
for semester_code, dates in response.context_data["events"]
|
||||
]
|
||||
assert events == [
|
||||
("A25", [self.news[0]]),
|
||||
("P25", [self.news[1], self.news[2]]),
|
||||
("A24", [self.news[3]]),
|
||||
("A20", [self.news[4]]),
|
||||
]
|
||||
@@ -31,6 +31,7 @@ from club.views import (
|
||||
ClubListView,
|
||||
ClubMailingView,
|
||||
ClubMembersView,
|
||||
ClubNewsView,
|
||||
ClubOldMembersView,
|
||||
ClubPageEditView,
|
||||
ClubPageHistView,
|
||||
@@ -136,4 +137,5 @@ urlpatterns = [
|
||||
PosterDeleteView.as_view(),
|
||||
name="poster_delete",
|
||||
),
|
||||
path("<int:club_id>/news/", ClubNewsView.as_view(), name="news_list"),
|
||||
]
|
||||
+35
-1
@@ -37,6 +37,7 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
|
||||
from django.core.paginator import InvalidPage, Paginator
|
||||
from django.db.models import F, Prefetch, Q, Sum
|
||||
from django.db.models.expressions import OuterRef, Subquery
|
||||
from django.db.models.functions import Length
|
||||
from django.http import Http404, StreamingHttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
@@ -71,7 +72,7 @@ from club.models import (
|
||||
MailingSubscription,
|
||||
Membership,
|
||||
)
|
||||
from com.models import Poster
|
||||
from com.models import NewsDate, Poster
|
||||
from com.views import (
|
||||
PosterCreateBaseView,
|
||||
PosterDeleteBaseView,
|
||||
@@ -80,6 +81,7 @@ from com.views import (
|
||||
)
|
||||
from core.auth.mixins import CanEditMixin, PermissionOrClubBoardRequiredMixin
|
||||
from core.models import Page, PageRev
|
||||
from core.utils import get_semester_code
|
||||
from core.views import BasePageEditView, DetailFormView, UseFragmentsMixin
|
||||
from core.views.mixins import (
|
||||
AllowFragment,
|
||||
@@ -134,6 +136,11 @@ class ClubTabsMixin(TabedViewMixin):
|
||||
"slug": "history",
|
||||
"name": _("History"),
|
||||
},
|
||||
{
|
||||
"url": reverse("club:news_list", kwargs={"club_id": self.object.id}),
|
||||
"slug": "events",
|
||||
"name": _("Events"),
|
||||
},
|
||||
]
|
||||
if self.request.user.can_edit(self.object):
|
||||
tab_list.extend(
|
||||
@@ -935,3 +942,30 @@ class PosterDeleteView(ClubTabsMixin, PosterDeleteBaseView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
|
||||
|
||||
|
||||
class ClubNewsView(PermissionRequiredMixin, ClubTabsMixin, DetailView):
|
||||
pk_url_kwarg = "club_id"
|
||||
model = Club
|
||||
permission_required = "club.view_club"
|
||||
template_name = "club/club_news_list.jinja"
|
||||
current_tab = "events"
|
||||
|
||||
def get_context_data(self, **kwargs: Any):
|
||||
events = list(
|
||||
self.object.news.viewable_by(self.request.user)
|
||||
.annotate(
|
||||
first_date=Subquery(
|
||||
NewsDate.objects.filter(news_id=OuterRef("pk"))
|
||||
.order_by("start_date")
|
||||
.values("start_date")[:1]
|
||||
),
|
||||
)
|
||||
.prefetch_related("dates")
|
||||
.order_by("-first_date")
|
||||
)
|
||||
return super().get_context_data(**kwargs) | {
|
||||
"events": itertools.groupby(
|
||||
events, key=lambda event: get_semester_code(event.first_date.date())
|
||||
),
|
||||
}
|
||||
+129
-24
@@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-10-19 19:45+0200\n"
|
||||
"POT-Creation-Date: 2026-10-25 12:04+0100\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@@ -394,7 +394,8 @@ msgstr ""
|
||||
"de la présidence."
|
||||
|
||||
#: club/models.py core/models.py counter/models.py eboutic/models.py
|
||||
#: election/models.py pedagogy/models.py sas/models.py trombi/models.py
|
||||
#: election/models.py pedagogy/models.py sas/models.py timetable/models.py
|
||||
#: trombi/models.py
|
||||
msgid "user"
|
||||
msgstr "utilisateur"
|
||||
|
||||
@@ -580,6 +581,7 @@ msgid "Role"
|
||||
msgstr "Rôle"
|
||||
|
||||
#: club/templates/club/club_members.jinja
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
#: club/templates/club/club_old_members.jinja
|
||||
#: core/templates/core/group_list.jinja core/templates/core/user_clubs.jinja
|
||||
msgid "Description"
|
||||
@@ -594,6 +596,34 @@ msgstr "Depuis"
|
||||
msgid "There are no members in this club."
|
||||
msgstr "Il n'y a pas de membres dans ce club."
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
#: com/templates/com/news_admin_list.jinja com/templates/com/news_edit.jinja
|
||||
#: com/templates/com/news_list.jinja core/templates/core/user_tools.jinja
|
||||
msgid "Create news"
|
||||
msgstr "Créer une nouvelle"
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
#, python-format
|
||||
msgid "Dates (%(nb_occurrences)s occurrences)"
|
||||
msgstr "Dates (%(nb_occurrences)s occurrences)"
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
msgid "Permalink"
|
||||
msgstr "Permalien"
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
#: com/templates/com/news_admin_list.jinja
|
||||
msgid "Summary"
|
||||
msgstr "Résumé"
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
msgid "No summary"
|
||||
msgstr "Pas de Résumé"
|
||||
|
||||
#: club/templates/club/club_news_list.jinja
|
||||
msgid "No detailed description"
|
||||
msgstr "Pas de description détaillée"
|
||||
|
||||
#: club/templates/club/club_old_members.jinja
|
||||
msgid "Club old members"
|
||||
msgstr "Anciens membres du club"
|
||||
@@ -1030,6 +1060,11 @@ msgstr "Anciens membres"
|
||||
msgid "History"
|
||||
msgstr "Historique"
|
||||
|
||||
#: club/views.py com/templates/com/news_admin_list.jinja
|
||||
#: core/templates/core/base/navbar.jinja
|
||||
msgid "Events"
|
||||
msgstr "Événements"
|
||||
|
||||
#: club/views.py core/templates/core/base/header.jinja core/views/user.py
|
||||
#: sas/templates/sas/picture.jinja trombi/views.py
|
||||
msgid "Tools"
|
||||
@@ -1317,11 +1352,6 @@ msgstr "Administration des nouvelles"
|
||||
msgid "News"
|
||||
msgstr "Nouvelles"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja com/templates/com/news_edit.jinja
|
||||
#: com/templates/com/news_list.jinja core/templates/core/user_tools.jinja
|
||||
msgid "Create news"
|
||||
msgstr "Créer une nouvelle"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja
|
||||
msgid "Weeklies"
|
||||
msgstr "Événements hebdomadaires"
|
||||
@@ -1336,10 +1366,6 @@ msgstr "Événements hebdomadaires affichées"
|
||||
msgid "Title"
|
||||
msgstr "Titre"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja
|
||||
msgid "Summary"
|
||||
msgstr "Résumé"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
||||
#: forum/templates/forum/forum.jinja sas/models.py
|
||||
msgid "Author"
|
||||
@@ -1367,11 +1393,6 @@ msgstr "Dépublier"
|
||||
msgid "Weeklies to moderate"
|
||||
msgstr "Nouvelles hebdomadaires à modérer"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja
|
||||
#: core/templates/core/base/navbar.jinja
|
||||
msgid "Events"
|
||||
msgstr "Événements"
|
||||
|
||||
#: com/templates/com/news_admin_list.jinja
|
||||
msgid "Displayed events"
|
||||
msgstr "Événements affichés"
|
||||
@@ -1869,7 +1890,7 @@ msgstr "département"
|
||||
msgid "dpt option"
|
||||
msgstr "Filière"
|
||||
|
||||
#: core/models.py pedagogy/models.py
|
||||
#: core/models.py pedagogy/models.py timetable/models.py
|
||||
msgid "semester"
|
||||
msgstr "semestre"
|
||||
|
||||
@@ -1994,6 +2015,18 @@ msgstr ""
|
||||
"Autorise les cotisants (ou les personnes whitelistées, si votre profil est "
|
||||
"caché) à accéder aux statistiques de votre compte AE"
|
||||
|
||||
#: core/models.py
|
||||
msgid "show your timetable to others"
|
||||
msgstr "montrez votre emploi du temps aux autres"
|
||||
|
||||
#: core/models.py
|
||||
msgid ""
|
||||
"Allow subscribers to see your timetable and to compare it to theirs (if you "
|
||||
"have filled it)"
|
||||
msgstr ""
|
||||
"Permet aux cotisants de voir votre emploi du temps et de le comparer au leur "
|
||||
"(si vous l'avez enregistré)"
|
||||
|
||||
#: core/models.py
|
||||
msgid "get a notification for every click"
|
||||
msgstr "avoir une notification pour chaque click"
|
||||
@@ -5201,7 +5234,7 @@ msgstr "heures THE"
|
||||
msgid "hours TE"
|
||||
msgstr "heures TE"
|
||||
|
||||
#: pedagogy/models.py
|
||||
#: pedagogy/models.py timetable/models.py
|
||||
msgid "ue"
|
||||
msgstr "UE"
|
||||
|
||||
@@ -5999,6 +6032,46 @@ msgstr "Membre existant"
|
||||
msgid "the groups that can create subscriptions"
|
||||
msgstr "les groupes pouvant créer des cotisations"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "timetable"
|
||||
msgstr "emploi du temps"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "timetables"
|
||||
msgstr "emplois du temps"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "weekday"
|
||||
msgstr "jour de la semaine"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "start at"
|
||||
msgstr "début"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "end at"
|
||||
msgstr "fin"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "course type"
|
||||
msgstr "type de cours"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "room"
|
||||
msgstr "salle"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "week group"
|
||||
msgstr "groupe de semaine"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "timetable slot"
|
||||
msgstr "créneau d'emploi du temps"
|
||||
|
||||
#: timetable/models.py
|
||||
msgid "timetable slots"
|
||||
msgstr "créneaux d'emploi du temps"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
msgid "Timetable generator"
|
||||
msgstr "Générateur d'emploi du temps"
|
||||
@@ -6029,8 +6102,44 @@ msgid "Generate"
|
||||
msgstr "Générer"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
msgid "Save to PNG"
|
||||
msgstr "Sauver en PNG"
|
||||
msgid "Save on your profile"
|
||||
msgstr "Enregistrer sur votre profil"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
msgid "Make this timetable public"
|
||||
msgstr "Rendre cet emploi du temps publique"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can change this at any time in <a href=\"%(url)s\">your preferences</a>"
|
||||
msgstr ""
|
||||
"Vous pouvez changer ceci n'importe quand dans <a href=\"%(url)s\">vos préférences</a>"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
msgid "Save timetable"
|
||||
msgstr "Enregistrer l'emploi du temps"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
#: trombi/templates/trombi/detail.jinja
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
|
||||
#: timetable/templates/timetable/generator.jinja
|
||||
msgid "Export to PNG"
|
||||
msgstr "Exporter en PNG"
|
||||
|
||||
#: timetable/views.py
|
||||
msgid "Timetables"
|
||||
msgstr "Emplois du temps"
|
||||
|
||||
#: timetable/views.py
|
||||
msgid "Generator"
|
||||
msgstr "Générateur"
|
||||
|
||||
#: timetable/views.py
|
||||
msgid "My timetables"
|
||||
msgstr "Mes emplois du temps"
|
||||
|
||||
#: trombi/models.py
|
||||
msgid "subscription deadline"
|
||||
@@ -6145,10 +6254,6 @@ msgstr "Fin des inscriptions : "
|
||||
msgid "Comment deadline: "
|
||||
msgstr "Fin des commentaires : "
|
||||
|
||||
#: trombi/templates/trombi/detail.jinja
|
||||
msgid "Export"
|
||||
msgstr "Exporter"
|
||||
|
||||
#: trombi/templates/trombi/detail.jinja
|
||||
msgid "Add user"
|
||||
msgstr "Ajouter une personne"
|
||||
|
||||
Reference in new issue
Block a user