From bd7e6b8ad471185c737c8923bd00af634881603c Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 13 Mar 2025 21:01:50 +0100 Subject: [PATCH 1/2] add table, add some translation --- .../wrapped/1/wrapped_view_club.html | 6 +- .../templates/wrapped/wrapped_list.html | 31 +++++--- apps/wrapped/views.py | 23 ++++-- locale/fr/LC_MESSAGES/django.po | 74 +++++++++++-------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html index 57da4a3f..6d295e9e 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html @@ -1,6 +1,6 @@ {% extends "wrapped/1/wrapped_base.html" %} {% comment %} -COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay +COPYRIGHT (C) 2018-2025 BDE ENS Paris-Saclay SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} {% load i18n pretty_money %} @@ -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 0a16fd92..c787fd96 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json @@ -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/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" From b293904525b6f9a98349c352f62d7397bb988c32 Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 13 Mar 2025 23:56:10 +0100 Subject: [PATCH 2/2] Another tables and doc --- apps/wrapped/views.py | 6 +- docs/_static/img/graphs/wrapped.svg | 118 ++++++++++++++++++++++++++++ docs/apps/index.rst | 3 + docs/apps/wrapped.rst | 108 +++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 docs/_static/img/graphs/wrapped.svg create mode 100644 docs/apps/wrapped.rst diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py index c787fd96..1300e1bc 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -20,9 +20,9 @@ class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Display all Wrapped, and classify by year """ model = Wrapped - tables =[ - lambda data: WrappedTable(data, prefix="public-"), - lambda data: WrappedTable(data, prefix="personnal-"), + 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")} 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 @@ + + + + + + +model_graph + + + +wrapped_models_Bde + + +     +    Bde     +     +id +     +     +AutoField +     +     +date_end +     +     +DateTimeField +     +     +date_start +     +     +DateTimeField +     +     +name +     +     +CharField +     + + + + +wrapped_models_Wrapped + + +     +    Wrapped     +     +id +     +     +AutoField +     +     +bde +     +     +ForeignKey (id) +     +     +note +     +     +ForeignKey (id) +     +     +data_json +     +     +TextField +     +     +generated +     +     +BooleanField +     +     +public +     +     +BooleanField +     + + + + +wrapped_models_Wrapped->wrapped_models_Bde + + + bde (+) + + + +note_models_notes_Note + + +   +Note +   + + + +wrapped_models_Wrapped->note_models_notes_Note + + + note (+) + + + +\n\n\n + + + 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.