diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html
index f9c2d178..6d295e9e 100644
--- a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html
+++ b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html
@@ -23,9 +23,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
let d1 = document.getElementById("consumer");
let d2 = document.getElementById("creditor");
if (con) { d1.textContent = {{ big_consumer | safe }}[0] + " " + gettext("with") + " " + {{ big_consumer | safe}}[1] + "€";}
- else { d1.textContent = gettext("Infortunately, you doesn't have consumer this year");};
+ else { d1.textContent = gettext({% trans "Infortunately, you doesn't have consumer this year" %});};
if (cre) { d2.textContent = {{ big_creancier | safe}}[0] + " " + gettext("with") + " " + {{ big_creancier | safe}}[1] + "€";}
- else { d2.textContent = gettext("Congratulations you are a real rat !"); };
+ else { d2.textContent = gettext({% trans "Congratulations you are a real rat !" %}); };
{% endblock %}
diff --git a/apps/wrapped/templates/wrapped/wrapped_list.html b/apps/wrapped/templates/wrapped/wrapped_list.html
index 28892de5..c15d4ada 100644
--- a/apps/wrapped/templates/wrapped/wrapped_list.html
+++ b/apps/wrapped/templates/wrapped/wrapped_list.html
@@ -6,17 +6,24 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% load i18n %}
{% block content %}
-
-
-
-
-
{{ title }}
-
-
- {% render_table table %}
-
-
-
+
+{% if tables|length > 0 %}
+
+
+ {% trans "My wrapped" %}
+
+ {% render_table tables.1 %}
+
+{% endif %}
+
+{% if tables|length > 0 %}
+
+
+ {% trans "Public wrapped" %}
+
+ {% render_table tables.0 %}
+
+{% endif %}
{% endblock %}
@@ -25,7 +32,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
let club_not_public = {{ club_not_public }};
if (club_not_public) { (addMsg("{% trans "Do not forget to ask permission to people who are in your wrapped before to make them public" %}", 'warning'));}
function refreshTable() {
- $("#wrapped_table").load(location.pathname + " #wrapped_table");
+ $("#wrapped_tables").load(location.pathname + " #wrapped_tables");
}
function copylink(id) {
diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py
index 8c7f834d..1300e1bc 100644
--- a/apps/wrapped/views.py
+++ b/apps/wrapped/views.py
@@ -6,7 +6,8 @@ import json
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
-from django_tables2.views import SingleTableView
+from django.views.generic.list import ListView
+from django_tables2.views import MultiTableMixin
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
@@ -14,21 +15,29 @@ from .models import Wrapped
from .tables import WrappedTable
-class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
+class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
"""
Display all Wrapped, and classify by year
"""
model = Wrapped
- table_class = WrappedTable
+ tables = [
+ lambda data: WrappedTable(data, prefix="public-"),
+ lambda data: WrappedTable(data, prefix="personnal-"),
+ ]
template_name = 'wrapped/wrapped_list.html'
extra_context = {'title': _("List of wrapped")}
def get_queryset(self, **kwargs):
return super().get_queryset(**kwargs).distinct()
- def get_table_data(self):
- return Wrapped.objects.filter(PermissionBackend.filter_queryset(
- self.request, Wrapped, "change", field='public')).distinct().order_by("-bde__date_start")
+ def get_tables_data(self):
+ return [
+ Wrapped.objects.filter(public=True),
+ Wrapped.objects
+ .filter(PermissionBackend.filter_queryset(self.request, Wrapped, "change", field='public'))
+ .distinct()
+ .order_by("-bde__date_start")
+ ]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
diff --git a/docs/_static/img/graphs/wrapped.svg b/docs/_static/img/graphs/wrapped.svg
new file mode 100644
index 00000000..e76497dc
--- /dev/null
+++ b/docs/_static/img/graphs/wrapped.svg
@@ -0,0 +1,118 @@
+
+
+
+
+
diff --git a/docs/apps/index.rst b/docs/apps/index.rst
index e16f5196..95315eb8 100644
--- a/docs/apps/index.rst
+++ b/docs/apps/index.rst
@@ -14,6 +14,7 @@ Applications de la Note Kfet 2020
logs
treasury
wei
+ wrapped
La Note Kfet 2020 est un projet Django, décomposé en applications.
Certaines applications sont développées uniquement pour ce projet, et sont indispensables,
@@ -69,4 +70,6 @@ Applications facultatives
Interface de gestion pour les trésorièr⋅es, émission de factures, remises de chèque, statistiques...
* `WEI `_ :
Interface de gestion du WEI.
+* `Wrapped `_ :
+ Récapitulatif personnalisé annuel de statitiques globales et personnelles.
diff --git a/docs/apps/wrapped.rst b/docs/apps/wrapped.rst
new file mode 100644
index 00000000..199572dc
--- /dev/null
+++ b/docs/apps/wrapped.rst
@@ -0,0 +1,108 @@
+Wrapped
+=======
+
+Cette application montre les statistiques annuelles des utilisateur·ice·s et/ou des clubs.
+
+Modèles
+-------
+
+Bde
+~~~
+
+Le modèle ``Bde`` contient des informations relatifs à un BDE :
+
+* ``name`` : ``CharField``, nom du BDE.
+* ``date_start`` : ``DateField``, date de prise de fonction du bureau BDE considéré.
+* ``date_end`` : ``DateField``, date de démission du bureau BDE considéré.
+
+Wrapped
+~~~~~~~
+
+Contient les informations sur un wrapped :
+
+* ``generated`` : ``BooleanField``, indique si le wrapped a été généré ou non.
+* ``public`` : ``BooleanField``, indique si le wrapped est visible de tous les utilisateur·ice·s ou non.
+* ``bde`` : ``ForeignKey(Bde)``, BDE auquel le wrapped correspond.
+* ``note`` : ``ForeignKey(Note)``, note à laquelle le wrapped correspond.
+* ``data_json`` : ``TextField``, diverses statistique concernant les notes durant le mandat BDE
+ considéré ou sur la NoteKfet dans sa globalité.
+
+Graphe des modèles
+~~~~~~~~~~~~~~~~~~
+
+.. image:: ../_static/img/graphs/wrapped.svg
+ :width: 960
+ :alt: Graphe des modèles de l'application Wrapped
+
+Fonctionnement
+--------------
+
+Création d'un BDE
+~~~~~~~~~~~~~~~~~
+
+Seul un⋅e respo info peut créer un BDE. Pour cela, se rendre dans l'onglet « Admin »., puis « BDE » et
+enfin « + Ajouter BDE ». Iel doit renseigner, les dates de début et de fin du bureau BDE ainsi que le
+nom de la liste.
+
+Génération des wrappeds
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Seul un·e respo info peut générer des wrappeds. Pour une utilisation annuelle classique, iel exécute la
+commande :
+
+``./manage.py generate_wrapped -b "bde_name" -u adh -c active``
+
+Pour une utilisation plus technique de cette commande se référer à sa documentation
+
+``./manage.py help generate_wrapped``
+
+Le script prend une dizaine de minutes pour générer tous les wrappeds.
+
+Créer ses propres wrappeds
+--------------------------
+
+Cette section est plus technique et s'addresse plutôt à des respos infos en cours de mandat qui voudrai
+faire les wrappeds de leur propre BDE.
+
+Contenu
+~~~~~~~
+
+Il est fortement conseillé de bien réfléchir à ce que l'on souhaite mettre sur un wrapped, plusieurs
+critères sont à prendre compte :
+
+* compréhension, est-ce que la donnée fait sens auprès des utilisateur·ice·s.
+* pertinence, est-ce que la donnée fonctionne pour un grand nombre d'utilisateur.
+* faisabilité, est-ce que le temps de calcul est suffisament rapide.
+* complexité, est-ce que c'est trop compliqué à coder.
+
+Script
+~~~~~~
+
+Le script *generate_wrapped* fonctionne de la manière suivante :
+
+* ``convert_to_note`` : en fonction des arguments d'entrée, il récupére toutes les notes dont le·s
+ wrapped·s va/vont être généré·s
+ ou regénéré·s.
+* ``global_data`` : le script génére ensuite des statistiques globales qui concernent pas qu'une seule
+ note (nombre de soirée, classement, etc).
+* ``unique_data`` : le script génére les statitiques uniques à chaque note, et rajoute des données
+ globales si nécessaire, pour chaque note on souhaite avoir un json avec toutes les données qui
+ seront dans le wrapped.
+* ``make_wrapped`` : enfin, le cas échéant, pour chaque bde, et pour chaque note, le wrapped est crée
+ ou modifié, et enregistré, s'il est crée il est par défault non public.
+
+Seules les fonctions ``global_data`` et ``unique_data`` sont à modifier, pour implementer un nouveau
+BDE.
+
+Template
+~~~~~~~~
+
+Il y a au moins deux templates a écrire pour chaque bde :
+
+* ``templates/wrapped/{bde_id}/wrapped_view_club.html``: le template pour les wrappeds des clubs
+* ``templates/wrapped/{bde_id}/wrapped_view_user.html``: le template pour les wrappeds des
+ utilisateur·ice·s
+
+Il est conseillé de suivre la même arborescence pour les fichiers statics (fonts personnalisées,
+images, css, etc). De même, il est conseillé de créé un fichier
+``templates/wrapped/{bde_id}/wrapped_base.html`` et d'étendre cette template.
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 79814474..3f4c22d7 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-02-25 13:47+0100\n"
+"POT-Creation-Date: 2025-03-13 21:08+0100\n"
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
"Last-Translator: bleizi \n"
"Language-Team: French \n"
@@ -865,7 +865,7 @@ msgstr "Taille maximale : 2 Mo"
msgid "This image cannot be loaded."
msgstr "Cette image ne peut pas être chargée."
-#: apps/member/forms.py:154 apps/member/views.py:103
+#: apps/member/forms.py:154 apps/member/views.py:117
#: apps/registration/forms.py:33 apps/registration/views.py:282
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
@@ -1194,11 +1194,11 @@ msgstr "Adhésion de {user} pour le club {club}"
msgid "The role {role} does not apply to the club {club}."
msgstr "Le rôle {role} ne s'applique pas au club {club}."
-#: apps/member/models.py:388 apps/member/views.py:745
+#: apps/member/models.py:388 apps/member/views.py:759
msgid "User is already a member of the club"
msgstr "L'utilisateur·rice est déjà membre du club"
-#: apps/member/models.py:400 apps/member/views.py:754
+#: apps/member/models.py:400 apps/member/views.py:768
msgid "User is not a member of the parent club"
msgstr "L'utilisateur·rice n'est pas membre du club parent"
@@ -1251,7 +1251,7 @@ msgid "Account #"
msgstr "Compte n°"
#: apps/member/templates/member/base.html:48
-#: apps/member/templates/member/base.html:62 apps/member/views.py:60
+#: apps/member/templates/member/base.html:62 apps/member/views.py:61
#: apps/registration/templates/registration/future_profile_detail.html:48
#: apps/wei/templates/wei/weimembership_form.html:117
msgid "Update Profile"
@@ -1312,8 +1312,8 @@ msgstr ""
"seront à nouveau possible."
#: apps/member/templates/member/club_alias.html:10
-#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:304
-#: apps/member/views.py:545
+#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:318
+#: apps/member/views.py:559
msgid "Note aliases"
msgstr "Alias de la note"
@@ -1505,51 +1505,51 @@ msgstr "Sauvegarder les changements"
msgid "Registrations"
msgstr "Inscriptions"
-#: apps/member/views.py:73 apps/registration/forms.py:23
+#: apps/member/views.py:74 apps/registration/forms.py:23
msgid "This address must be valid."
msgstr "Cette adresse doit être valide."
-#: apps/member/views.py:140
+#: apps/member/views.py:154
msgid "Profile detail"
msgstr "Détails de l'utilisateur⋅rice"
-#: apps/member/views.py:206
+#: apps/member/views.py:220
msgid "Search user"
msgstr "Chercher un·e utilisateur·rice"
-#: apps/member/views.py:258
+#: apps/member/views.py:272
msgid "Note friendships"
msgstr "Amitiés note"
-#: apps/member/views.py:328
+#: apps/member/views.py:342
msgid "Update note picture"
msgstr "Modifier la photo de la note"
-#: apps/member/views.py:377
+#: apps/member/views.py:391
msgid "Manage auth token"
msgstr "Gérer les jetons d'authentification"
-#: apps/member/views.py:404
+#: apps/member/views.py:418
msgid "Create new club"
msgstr "Créer un nouveau club"
-#: apps/member/views.py:423
+#: apps/member/views.py:437
msgid "Search club"
msgstr "Chercher un club"
-#: apps/member/views.py:461
+#: apps/member/views.py:475
msgid "Club detail"
msgstr "Détails du club"
-#: apps/member/views.py:573
+#: apps/member/views.py:587
msgid "Update club"
msgstr "Modifier le club"
-#: apps/member/views.py:607
+#: apps/member/views.py:621
msgid "Add new member to the club"
msgstr "Ajouter un·e nouvelleau membre au club"
-#: apps/member/views.py:736 apps/wei/views.py:991
+#: apps/member/views.py:750 apps/wei/views.py:991
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@@ -1557,19 +1557,19 @@ msgstr ""
"Cet⋅te utilisateur⋅rice n'a pas assez d'argent pour rejoindre ce club et ne "
"peut pas avoir un solde négatif."
-#: apps/member/views.py:758
+#: apps/member/views.py:772
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
-#: apps/member/views.py:763
+#: apps/member/views.py:777
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
-#: apps/member/views.py:913
+#: apps/member/views.py:927
msgid "Manage roles of an user in the club"
msgstr "Gérer les rôles d'un⋅e utilisateur⋅rice dans le club"
-#: apps/member/views.py:938
+#: apps/member/views.py:952
msgid "Members of the club"
msgstr "Membres du club"
@@ -2084,7 +2084,7 @@ msgid "Button displayed"
msgstr "Bouton affiché"
#: apps/note/templates/note/transactiontemplate_list.html:100
-#: apps/wrapped/templates/wrapped/wrapped_list.html:63
+#: apps/wrapped/templates/wrapped/wrapped_list.html:70
msgid "An error occured"
msgstr "Une erreur s'est produite"
@@ -3662,6 +3662,14 @@ msgstr "soirée·s organisée·s"
msgid "distinct members"
msgstr "Membres distinct·e·s"
+#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:26
+msgid "Infortunately, you doesn't have consumer this year"
+msgstr "Malheureusement, tu n'as pas de consommateur cette année"
+
+#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:28
+msgid "Congratulations you are a real rat !"
+msgstr "Félicitations, tu es un vrai rat !"
+
#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:13
msgid "You participate to the wei: "
msgstr "Tu as participé au wei : "
@@ -3699,7 +3707,15 @@ msgstr "avec"
msgid "Your expenses to BDE: "
msgstr "Tes dépenses au BDE : "
-#: apps/wrapped/templates/wrapped/wrapped_list.html:26
+#: apps/wrapped/templates/wrapped/wrapped_list.html:13
+msgid "My wrapped"
+msgstr "Mes wrapped"
+
+#: apps/wrapped/templates/wrapped/wrapped_list.html:22
+msgid "Public wrapped"
+msgstr "Wrapped public"
+
+#: apps/wrapped/templates/wrapped/wrapped_list.html:33
msgid ""
"Do not forget to ask permission to people who are in your wrapped before to "
"make them public"
@@ -3707,19 +3723,19 @@ msgstr ""
"N'oublies pas de demander la permission des personnes apparaissant dans un "
"wrapped avant de le rendre public"
-#: apps/wrapped/templates/wrapped/wrapped_list.html:33
+#: apps/wrapped/templates/wrapped/wrapped_list.html:40
msgid "Link copied"
msgstr "Lien copié"
-#: apps/wrapped/templates/wrapped/wrapped_list.html:58
+#: apps/wrapped/templates/wrapped/wrapped_list.html:65
msgid "Wrapped is private"
msgstr "Le wrapped est privé"
-#: apps/wrapped/templates/wrapped/wrapped_list.html:59
+#: apps/wrapped/templates/wrapped/wrapped_list.html:66
msgid "Wrapped is public"
msgstr "Le wrapped est public"
-#: apps/wrapped/views.py:24
+#: apps/wrapped/views.py:28
msgid "List of wrapped"
msgstr "Liste des wrapped"