Compare commits

...

21 Commits

Author SHA1 Message Date
mcngnt c725c68a9e Merge branch 'survey_wei_2024' into 'main'
update hardcoded

See merge request bde/nk20!273
2024-09-12 11:49:41 +02:00
mcngnt c1a353963a handle hardcoded corrected 2024-09-12 11:36:37 +02:00
quark 9162319734 Merge branch 'quark-main-patch-05186' into 'main'
Update views.py (don't display forced blocked note, it's just temporary patch,...

See merge request bde/nk20!272
2024-09-09 21:02:19 +02:00
quark 5d2a8e9b79 Update views.py (don't display forced blocked note, it's just temporary patch, we need to block these note in models too) 2024-09-09 19:05:53 +02:00
bleizi 33c94d0720 Merge branch 'non-BDE-members' into 'main'
Allow non-BDE members to use the note

See merge request bde/nk20!268
2024-09-05 23:15:04 +02:00
bleizi 5040e8e8ea Merge branch 'continuous-intergration' into 'main'
continuous-intergration

See merge request bde/nk20!271
2024-09-05 20:54:40 +02:00
Nicolas Margulies c5697c4cb4 don't hide the transfer tab 2024-09-05 20:54:23 +02:00
nicomarg e188c5a153 Merge branch 'mail' into 'main'
mail

Closes #119

See merge request bde/nk20!270
2024-09-05 20:29:30 +02:00
bleizi 94e1fdc93a
add ubuntu 24.4 in tox.ini and remove debian bullseye in gitlab-ci 2024-09-05 20:19:46 +02:00
Nicolas Margulies d1ef367bab Permissions for child clubs, also changed spaces for tabs 2024-09-05 20:17:45 +02:00
bleizi 0fbb19c5fd
limite mail sending to 10 per minute and purge fail mail log 2024-09-05 19:48:54 +02:00
mcngnt 21cbf2b21a Merge branch 'survey_wei_2024' into 'main'
Survey wei 2024

See merge request bde/nk20!269
2024-08-29 23:10:57 +02:00
nicomarg 361de9f8b4 more bug fixing 2024-08-29 21:06:34 +02:00
nicomarg e2426bd6a6 Bugfix 2024-08-29 20:03:43 +02:00
nicomarg 7fea619a9f add permission to make transfers with members of your club 2024-08-29 20:02:06 +02:00
nicomarg 7b5eefcc0a Update 2 files
- /apps/registration/views.py
- /apps/permission/fixtures/initial.json
2024-08-29 19:23:26 +02:00
mcngnt e4aa16986f Merge branch 'survey_wei_2024' into 'main'
linting

See merge request bde/nk20!267
2024-08-29 19:12:23 +02:00
mcngnt dd675b3676 Merge branch 'survey_wei_2024' into 'main'
Survey wei 2024

See merge request bde/nk20!266
2024-08-29 14:45:28 +02:00
korenstin a5df98224f Merge branch 'migration-django-4-2' into 'main'
Migration django 4 2

See merge request bde/nk20!265
2024-08-29 10:49:44 +02:00
korenstin 2cb9ac8735 replace "…" -> "..." (#130) and disable sorting on certain columns (#129) 2024-08-29 10:19:06 +02:00
korenstin 35d4849a28 fix Oauth 2024-08-29 00:43:33 +02:00
22 changed files with 4495 additions and 4421 deletions

View File

@ -7,21 +7,6 @@ stages:
variables: variables:
GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_STRATEGY: recursive
# Debian Bullseye
py39-django42:
stage: test
image: debian:bullseye
before_script:
- >
apt-get update &&
apt-get install --no-install-recommends -y
python3-django python3-django-crispy-forms
python3-django-extensions python3-django-filters python3-django-polymorphic
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
python3-bs4 python3-setuptools tox texlive-xetex
script: tox -e py39-django42
# Ubuntu 22.04 # Ubuntu 22.04
py310-django42: py310-django42:
stage: test stage: test
@ -54,8 +39,6 @@ py311-django42:
python3-bs4 python3-setuptools tox texlive-xetex python3-bs4 python3-setuptools tox texlive-xetex
script: tox -e py311-django42 script: tox -e py311-django42
linters: linters:
stage: quality-assurance stage: quality-assurance
image: debian:bookworm image: debian:bookworm

View File

@ -265,12 +265,11 @@ class ActivityEntryView(LoginRequiredMixin, SingleTableMixin, TemplateView):
# Keep only users that have a note # Keep only users that have a note
note_qs = note_qs.filter(note__noteuser__isnull=False) note_qs = note_qs.filter(note__noteuser__isnull=False)
# Keep only members # Keep only valid members
note_qs = note_qs.filter( note_qs = note_qs.filter(
note__noteuser__user__memberships__club=activity.attendees_club, note__noteuser__user__memberships__club=activity.attendees_club,
note__noteuser__user__memberships__date_start__lte=timezone.now(), note__noteuser__user__memberships__date_start__lte=timezone.now(),
note__noteuser__user__memberships__date_end__gte=timezone.now(), note__noteuser__user__memberships__date_end__gte=timezone.now()).exclude(note__inactivity_reason='forced')
)
# Filter with permission backend # Filter with permission backend
note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request, Alias, "view")) note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request, Alias, "view"))

View File

@ -42,12 +42,12 @@ class UserTable(tables.Table):
""" """
alias = tables.Column() alias = tables.Column()
section = tables.Column(accessor='profile__section') section = tables.Column(accessor='profile__section', orderable=False)
# Override the column to let replace the URL # Override the column to let replace the URL
email = tables.EmailColumn(linkify=lambda record: "mailto:{}".format(record.email)) email = tables.EmailColumn(linkify=lambda record: "mailto:{}".format(record.email))
balance = tables.Column(accessor='note__balance', verbose_name=_("Balance")) balance = tables.Column(accessor='note__balance', verbose_name=_("Balance"), orderable=False)
def render_email(self, record, value): def render_email(self, record, value):
# Replace the email by a dash if the user can't see the profile detail # Replace the email by a dash if the user can't see the profile detail

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{{ title }} {{ title }}
</h3> </h3>
<div class="card-body"> <div class="card-body">
<input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note"> <input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note...">
<div class="form-check"> <div class="form-check">
<label class="form-check-label" for="only_active"> <label class="form-check-label" for="only_active">
<input type="checkbox" class="checkboxinput form-check-input" id="only_active" <input type="checkbox" class="checkboxinput form-check-input" id="only_active"

View File

@ -260,11 +260,13 @@ class ButtonTable(tables.Table):
text=_('edit'), text=_('edit'),
accessor='pk', accessor='pk',
verbose_name=_("Edit"), verbose_name=_("Edit"),
orderable=False,
) )
hideshow = tables.Column( hideshow = tables.Column(
verbose_name=_("Hide/Show"), verbose_name=_("Hide/Show"),
accessor="pk", accessor="pk",
orderable=False,
attrs={ attrs={
'td': { 'td': {
'class': 'col-sm-1', 'class': 'col-sm-1',
@ -276,7 +278,8 @@ class ButtonTable(tables.Table):
delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE, delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
extra_context={"delete_trans": _('delete')}, extra_context={"delete_trans": _('delete')},
attrs={'td': {'class': 'col-sm-1'}}, attrs={'td': {'class': 'col-sm-1'}},
verbose_name=_("Delete"), ) verbose_name=_("Delete"),
orderable=False, )
def render_amount(self, value): def render_amount(self, value):
return pretty_money(value) return pretty_money(value)

View File

@ -3752,6 +3752,54 @@
"description": "Modifier bouffe" "description": "Modifier bouffe"
} }
}, },
{
"model": "permission.permission",
"pk": 239,
"fields": {
"model": [
"note",
"alias"
],
"query": "[\"AND\", {\"note__noteuser__user__memberships__club\": [\"club\"], \"note__noteuser__user__memberships__date_start__lte\": [\"today\"], \"note__noteuser__user__memberships__date_end__gte\": [\"today\"]}, {\"note__is_active\": true}]",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir les alias des notes des adhérent⋅es du club"
}
},
{
"model": "permission.permission",
"pk": 240,
"fields": {
"model": [
"note",
"alias"
],
"query": "[\"AND\", {\"note__noteuser__user__memberships__club\": [\"club\", \"parent_club\"], \"note__noteuser__user__memberships__date_start__lte\": [\"today\"], \"note__noteuser__user__memberships__date_end__gte\": [\"today\"]}, {\"note__is_active\": true}]",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir les alias des notes des adhérent⋅es du club parent"
}
},
{
"model": "permission.permission",
"pk": 241,
"fields": {
"model": [
"auth",
"user"
],
"query": "[\"AND\", {\"memberships__club\": [\"club\", \"parent_club\"], \"memberships__date_start__lte\": [\"today\"], \"memberships__date_end__gte\": [\"today\"]}, {\"note__is_active\": true}]",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir les utilisateurs adhérents au club parent"
}
},
{ {
"model": "permission.role", "model": "permission.role",
"pk": 1, "pk": 1,
@ -3851,6 +3899,19 @@
"for_club": null, "for_club": null,
"name": "Membre de club", "name": "Membre de club",
"permissions": [ "permissions": [
1,
2,
3,
4,
5,
7,
8,
9,
10,
11,
12,
13,
14,
22 22
] ]
} }
@ -3913,7 +3974,10 @@
142, 142,
182, 182,
184, 184,
185 185,
239,
240,
241
] ]
} }
}, },

View File

@ -135,18 +135,18 @@ class Permission(models.Model):
# A json encoded Q object with the following grammar # A json encoded Q object with the following grammar
# query -> [] | {} (the empty query representing all objects) # query -> [] | {} (the empty query representing all objects)
# query -> ["AND", query, …] AND multiple queries # query -> ["AND", query, ...] AND multiple queries
# | ["OR", query, …] OR multiple queries # | ["OR", query, ...] OR multiple queries
# | ["NOT", query] Opposite of query # | ["NOT", query] Opposite of query
# query -> {key: value, …} A list of fields and values of a Q object # query -> {key: value, ...} A list of fields and values of a Q object
# key -> string A field name # key -> string A field name
# value -> int | string | bool | null Literal values # value -> int | string | bool | null Literal values
# | [parameter, …] A parameter. See compute_param for more details. # | [parameter, ...] A parameter. See compute_param for more details.
# | {"F": oper} An F object # | {"F": oper} An F object
# oper -> [string, …] A parameter. See compute_param for more details. # oper -> [string, ...] A parameter. See compute_param for more details.
# | ["ADD", oper, …] Sum multiple F objects or literal # | ["ADD", oper, ...] Sum multiple F objects or literal
# | ["SUB", oper, oper] Substract two F objects or literal # | ["SUB", oper, oper] Substract two F objects or literal
# | ["MUL", oper, …] Multiply F objects or literals # | ["MUL", oper, ...] Multiply F objects or literals
# | int | string | bool | null Literal values # | int | string | bool | null Literal values
# | ["F", string] A field # | ["F", string] A field
# #

View File

@ -300,9 +300,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
# join_bde = True # join_bde = True
# join_kfet = True # join_kfet = True
if not join_bde: if not (join_bde or any(b for _, b in join_clubs)):
# This software belongs to the BDE. # This software belongs to the BDE.
form.add_error('join_bde', _("You must join the BDE.")) form.add_error('join_bde', _("You must join a club."))
return super().form_invalid(form)
if join_kfet and not join_bde:
form.add_error('join_bde', _("You must also join the parent club BDE."))
return super().form_invalid(form) return super().form_invalid(form)
# Calculate required registration fee # Calculate required registration fee

View File

@ -37,6 +37,7 @@ class InvoiceTable(tables.Table):
args=[A('id')], args=[A('id')],
verbose_name=_("delete"), verbose_name=_("delete"),
text=_("Delete"), text=_("Delete"),
orderable=False,
attrs={ attrs={
'th': { 'th': {
'id': 'delete-membership-header' 'id': 'delete-membership-header'
@ -70,6 +71,7 @@ class RemittanceTable(tables.Table):
verbose_name=_("View"), verbose_name=_("View"),
args=[A("pk")], args=[A("pk")],
text=_("View"), text=_("View"),
orderable=False,
attrs={ attrs={
'a': {'class': 'btn btn-primary'} 'a': {'class': 'btn btn-primary'}
}, ) }, )
@ -97,6 +99,7 @@ class SpecialTransactionTable(tables.Table):
verbose_name=_("Remittance"), verbose_name=_("Remittance"),
args=[A("specialtransactionproxy__pk")], args=[A("specialtransactionproxy__pk")],
text=_("Add"), text=_("Add"),
orderable=False,
attrs={ attrs={
'a': {'class': 'btn btn-primary'} 'a': {'class': 'btn btn-primary'}
}, ) }, )
@ -105,6 +108,7 @@ class SpecialTransactionTable(tables.Table):
verbose_name=_("Remittance"), verbose_name=_("Remittance"),
args=[A("specialtransactionproxy__pk")], args=[A("specialtransactionproxy__pk")],
text=_("Remove"), text=_("Remove"),
orderable=False,
attrs={ attrs={
'a': {'class': 'btn btn-primary btn-danger'} 'a': {'class': 'btn btn-primary btn-danger'}
}, ) }, )
@ -130,10 +134,12 @@ class SogeCreditTable(tables.Table):
amount = tables.Column( amount = tables.Column(
verbose_name=_("Amount"), verbose_name=_("Amount"),
orderable=False,
) )
valid = tables.Column( valid = tables.Column(
verbose_name=_("Valid"), verbose_name=_("Valid"),
orderable=False,
) )
def render_amount(self, value): def render_amount(self, value):

View File

@ -82,7 +82,7 @@ WORDS = {
5: "La quoi ?" 5: "La quoi ?"
}], }],
"kokarde": ["Qu'est-ce que le mot Kokarde t'évoque ?", { "kokarde": ["Qu'est-ce que le mot Kokarde t'évoque ?", {
1: "Vraiment pas mon truc les soirées", 1: "Vraiment pas mon truc les soirées...",
2: "Bof, je viens pour manger et je repars aussitôt", 2: "Bof, je viens pour manger et je repars aussitôt",
3: "Je kiffe, good vibes", 3: "Je kiffe, good vibes",
4: "Perso, je ne m'arrêterai pas de danser sur la piste !", 4: "Perso, je ne m'arrêterai pas de danser sur la piste !",
@ -117,15 +117,15 @@ WORDS = {
5: "Je pourrais en faire à n'importe qui. Pourquoi ne pas créer le club Câl[ENS] ?" 5: "Je pourrais en faire à n'importe qui. Pourquoi ne pas créer le club Câl[ENS] ?"
}], }],
"vomi": ["Quel est ton rapport au vomi ?", { "vomi": ["Quel est ton rapport au vomi ?", {
1: "C'est compliqué", 1: "C'est compliqué...",
2: "Jamais je ne vomis mais je nettoie quand mes potes vomissent", 2: "Jamais je ne vomis mais je nettoie quand mes potes vomissent",
3: "Jamais je ne vomis et jamais je ne nettoie celui de quelqu'un d'autre", 3: "Jamais je ne vomis et jamais je ne nettoie celui de quelqu'un d'autre",
4: "Je vomis quelquefois, ça arrive, faites pas cette tête, mais je fins toujours par nettoyer !", 4: "Je vomis quelquefois, ça arrive, faites pas cette tête, mais je fins toujours par nettoyer !",
5: "Je vomis à chaque soirée et ce n'est jamais moi qui nettoie" 5: "Je vomis à chaque soirée et ce n'est jamais moi qui nettoie"
}], }],
"kfet": ["Qu'est ce que la Kfet t'évoque ?", { "kfet": ["Qu'est ce que la Kfet t'évoque ?", {
1: "La Kfet, quel lieu de dépravé⋅es sérieux", 1: "La Kfet, quel lieu de dépravé⋅es sérieux...",
2: "C'est un endroit à l'hygiène plus que douteuse", 2: "C'est un endroit à l'hygiène plus que douteuse...",
3: "Téma les prix des boissons et des snacks, c'est aberrant !", 3: "Téma les prix des boissons et des snacks, c'est aberrant !",
4: "En vrai, c'est cool, petit billard, petit canapé, chill !", 4: "En vrai, c'est cool, petit billard, petit canapé, chill !",
5: "Banger, j'y reste jusqu'à la fin de mes jours" 5: "Banger, j'y reste jusqu'à la fin de mes jours"
@ -147,7 +147,7 @@ WORDS = {
"scolarite": ["Comment tu vois ton cursus à l'ENS ?", { "scolarite": ["Comment tu vois ton cursus à l'ENS ?", {
1: "La tranquillité et le travail", 1: "La tranquillité et le travail",
2: "On va s'amuser tout en bossant", 2: "On va s'amuser tout en bossant",
3: "Ça va profiter et réviser au dernier moment pour les exams", 3: "Ça va profiter et réviser au dernier moment pour les exams...",
4: "Nous festoierons sans songer aux conséquences", 4: "Nous festoierons sans songer aux conséquences",
5: "Je ne vois qu'une seule issue : la débauche" 5: "Je ne vois qu'une seule issue : la débauche"
}] }]

View File

@ -276,13 +276,27 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys
# Don't manage hardcoded people # Don't manage hardcoded people
surveys = [s for s in surveys if s.registration.bus is not None] # surveys = [s for s in surveys if s.bus_id != None]
# surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded]
# surveys = [s for s in surveys if s.registration.user_id in free_users]
# hardcoded_first_year_mb = WEIMembership.objects.filter(bus != None,registration__first_year=True)
# hardcoded_first_year = hardcoded_first_year_mb.values_list('user__id', 'bus__id')
hardcoded_first_year_mb = WEIMembership.objects.filter(registration__first_year=True)
hardcoded_first_year = {mb.user.id if mb.bus else None: mb.bus.id if mb.bus else None for mb in hardcoded_first_year_mb}
# Reset previous algorithm run # Reset previous algorithm run
for survey in surveys: for survey in surveys:
survey.free() survey.free()
if survey.registration.user_id in hardcoded_first_year.keys():
survey.select_bus(hardcoded_first_year[s.registration.user_id])
survey.save() survey.save()
non_men = [s for s in surveys if s.registration.gender != 'male'] non_men = [s for s in surveys if s.registration.gender != 'male']
men = [s for s in surveys if s.registration.gender == 'male'] men = [s for s in surveys if s.registration.gender == 'male']
@ -290,7 +304,8 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
registrations = self.get_registrations() registrations = self.get_registrations()
non_men_total = registrations.filter(~Q(gender='male')).count() non_men_total = registrations.filter(~Q(gender='male')).count()
for bus in self.get_buses(): for bus in self.get_buses():
free_seats = bus.size - WEIMembership.objects.filter(bus=bus).count() free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats) quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats)
tqdm_obj = None tqdm_obj = None
@ -303,7 +318,7 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
quotas = {} quotas = {}
for bus in self.get_buses(): for bus in self.get_buses():
free_seats = bus.size - WEIMembership.objects.filter(bus=bus).count() free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk) free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
quotas[bus] = free_seats quotas[bus] = free_seats

View File

@ -32,7 +32,7 @@ Applications indispensables
* `Note <note>`_ : * `Note <note>`_ :
Les notes associées à des utilisateur⋅rices ou des clubs. Les notes associées à des utilisateur⋅rices ou des clubs.
* `Activity <activity>`_ : * `Activity <activity>`_ :
La gestion des activités (créations, gestion, entrées,) La gestion des activités (créations, gestion, entrées, ...)
* `Permission <permission>`_ : * `Permission <permission>`_ :
Backend de droits, limites les pouvoirs des utilisateur⋅rices Backend de droits, limites les pouvoirs des utilisateur⋅rices
* `API <../api>`_ : * `API <../api>`_ :
@ -64,9 +64,9 @@ Applications facultatives
* ``cas-server`` * ``cas-server``
Serveur central d'authentification, permet d'utiliser son compte de la NoteKfet2020 pour se connecter à d'autre application ayant intégrer un client. Serveur central d'authentification, permet d'utiliser son compte de la NoteKfet2020 pour se connecter à d'autre application ayant intégrer un client.
* `Scripts <https://gitlab.crans.org/bde/nk20-scripts>`_ * `Scripts <https://gitlab.crans.org/bde/nk20-scripts>`_
Ensemble de commande `./manage.py` pour la gestion de la note: import de données, verification d'intégrité, etc Ensemble de commande `./manage.py` pour la gestion de la note: import de données, verification d'intégrité, etc...
* `Treasury <treasury>`_ : * `Treasury <treasury>`_ :
Interface de gestion pour les trésorièr⋅es, émission de factures, remises de chèque, statistiques ... Interface de gestion pour les trésorièr⋅es, émission de factures, remises de chèque, statistiques...
* `WEI <wei>`_ : * `WEI <wei>`_ :
Interface de gestion du WEI. Interface de gestion du WEI.

View File

@ -43,7 +43,7 @@ l'utilisateur⋅rice, utiles pour l'adhésion au BDE :
* ``address`` : ``CharField``, adresse physique de l'utilisateur⋅rice * ``address`` : ``CharField``, adresse physique de l'utilisateur⋅rice
* ``paid`` : ``BooleanField``, indique si l'utilisateur⋅rice normalien⋅ne est rémunéré⋅e ou non (utile pour différencier les montants d'adhésion aux clubs) * ``paid`` : ``BooleanField``, indique si l'utilisateur⋅rice normalien⋅ne est rémunéré⋅e ou non (utile pour différencier les montants d'adhésion aux clubs)
* ``phone_number`` : ``CharField``, numéro de téléphone de l'utilisateur⋅rice * ``phone_number`` : ``CharField``, numéro de téléphone de l'utilisateur⋅rice
* ``section`` : ``CharField``, section de l'ENS à laquelle appartient l'utilisateur⋅rice (exemple : 1A0,) * ``section`` : ``CharField``, section de l'ENS à laquelle appartient l'utilisateur⋅rice (exemple : 1A0, ...)
Clubs Clubs
~~~~~ ~~~~~
@ -101,7 +101,7 @@ Adhésions
La Note Kfet offre la possibilité aux clubs de gérer l'adhésion de leurs membres. En plus de réguler les cotisations La Note Kfet offre la possibilité aux clubs de gérer l'adhésion de leurs membres. En plus de réguler les cotisations
des adhérent⋅es, des permissions sont octroyées sur la note en fonction des rôles au sein des clubs. Un rôle est une des adhérent⋅es, des permissions sont octroyées sur la note en fonction des rôles au sein des clubs. Un rôle est une
fonction occupée au sein d'un club (Trésorièr⋅e de club, président⋅e de club, GC Kfet, Res[pot], respo info,). fonction occupée au sein d'un club (Trésorièr⋅e de club, président⋅e de club, GC Kfet, Res[pot], respo info, ...).
Une adhésion attribue à un⋅e adhérent⋅e ses rôles. Les rôles fournissent les permissions. Par exemple, læ trésorièr⋅e d'un Une adhésion attribue à un⋅e adhérent⋅e ses rôles. Les rôles fournissent les permissions. Par exemple, læ trésorièr⋅e d'un
club a le droit de faire des transferts de et vers la note du club, tant que la source reste au-dessus de -50 €. club a le droit de faire des transferts de et vers la note du club, tant que la source reste au-dessus de -50 €.
Une adhésion est considérée comme valide si la date du jour est comprise (au sens large) entre les dates de début et Une adhésion est considérée comme valide si la date du jour est comprise (au sens large) entre les dates de début et

View File

@ -49,7 +49,7 @@ Une fois l'inscription validée, détail de ce qu'il se passe :
lui octroyant un faible nombre de permissions de base, telles que la visualisation de son compte. lui octroyant un faible nombre de permissions de base, telles que la visualisation de son compte.
* On adhère la personne au club Kfet si cela est demandé, l'adhésion commence aujourd'hui. Iel dispose d'un unique rôle : * On adhère la personne au club Kfet si cela est demandé, l'adhésion commence aujourd'hui. Iel dispose d'un unique rôle :
« Adhérent⋅e Kfet » , lui octroyant un nombre un peu plus conséquent de permissions basiques, telles que la possibilité de « Adhérent⋅e Kfet » , lui octroyant un nombre un peu plus conséquent de permissions basiques, telles que la possibilité de
faire des transactions, d'accéder aux activités, au WEI, faire des transactions, d'accéder aux activités, au WEI, ...
* Si læ nouvelleau membre a indiqué avoir ouvert un compte à la société générale, alors les transactions sont invalidées, * Si læ nouvelleau membre a indiqué avoir ouvert un compte à la société générale, alors les transactions sont invalidées,
la note n'est pas débitée (commence alors à 0 €). la note n'est pas débitée (commence alors à 0 €).

View File

@ -3744,8 +3744,8 @@ msgid "FAQ (FR)"
msgstr "FAQ (FR)" msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15 #: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name" msgid "Search by attribute such as name..."
msgstr "Suche nach Attributen wie Name" msgstr "Suche nach Attributen wie Name..."
#: note_kfet/templates/base_search.html:23 #: note_kfet/templates/base_search.html:23
msgid "There is no results." msgid "There is no results."

View File

@ -3694,8 +3694,8 @@ msgid "FAQ (FR)"
msgstr "FAQ (FR)" msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15 #: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name" msgid "Search by attribute such as name..."
msgstr "Buscar con atributo, como el nombre" msgstr "Buscar con atributo, como el nombre..."
#: note_kfet/templates/base_search.html:23 #: note_kfet/templates/base_search.html:23
msgid "There is no results." msgid "There is no results."

View File

@ -1930,8 +1930,8 @@ msgstr "Consommer"
#: apps/note/templates/note/conso_form.html:43 #: apps/note/templates/note/conso_form.html:43
#: apps/note/templates/note/transaction_form.html:69 #: apps/note/templates/note/transaction_form.html:69
#: apps/note/templates/note/transaction_form.html:96 #: apps/note/templates/note/transaction_form.html:96
msgid "Name or alias" msgid "Name or alias..."
msgstr "Pseudo ou alias" msgstr "Pseudo ou alias..."
#: apps/note/templates/note/conso_form.html:53 #: apps/note/templates/note/conso_form.html:53
msgid "Select consumptions" msgid "Select consumptions"
@ -2031,8 +2031,8 @@ msgid "Current price"
msgstr "Prix actuel" msgstr "Prix actuel"
#: apps/note/templates/note/transactiontemplate_list.html:13 #: apps/note/templates/note/transactiontemplate_list.html:13
msgid "Name of the button" msgid "Name of the button..."
msgstr "Nom du bouton" msgstr "Nom du bouton..."
#: apps/note/templates/note/transactiontemplate_list.html:15 #: apps/note/templates/note/transactiontemplate_list.html:15
msgid "New button" msgid "New button"
@ -3386,8 +3386,8 @@ msgstr ""
"coût d'adhésion." "coût d'adhésion."
#: apps/wei/templates/wei/weimembership_list.html:27 #: apps/wei/templates/wei/weimembership_list.html:27
msgid "View unvalidated registrations" msgid "View unvalidated registrations..."
msgstr "Voir les inscriptions non validées" msgstr "Voir les inscriptions non validées..."
#: apps/wei/templates/wei/weiregistration_confirm_delete.html:16 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:16
msgid "This registration is already validated and can't be deleted." msgid "This registration is already validated and can't be deleted."
@ -3407,8 +3407,8 @@ msgid "There is no pre-registration found with this pattern."
msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée." msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée."
#: apps/wei/templates/wei/weiregistration_list.html:27 #: apps/wei/templates/wei/weiregistration_list.html:27
msgid "View validated memberships…" msgid "View validated membershipis..."
msgstr "Voir les adhésions validées" msgstr "Voir les adhésions validées..."
#: apps/wei/views.py:58 #: apps/wei/views.py:58
msgid "Search WEI" msgid "Search WEI"
@ -3673,8 +3673,8 @@ msgid "FAQ (FR)"
msgstr "FAQ (FR)" msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15 #: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name" msgid "Search by attribute such as name..."
msgstr "Chercher par un attribut tel que le nom" msgstr "Chercher par un attribut tel que le nom..."
#: note_kfet/templates/base_search.html:23 #: note_kfet/templates/base_search.html:23
msgid "There is no results." msgid "There is no results."

View File

@ -9,6 +9,7 @@ MAILTO=notekfet2020@lists.crans.org
* * * * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail -c 1 -v 0 * * * * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail -c 1 -v 0
* * * * * root cd /var/www/note_kfet && env/bin/python manage.py retry_deferred -c 1 -v 0 * * * * * root cd /var/www/note_kfet && env/bin/python manage.py retry_deferred -c 1 -v 0
00 0 * * * root cd /var/www/note_kfet && env/bin/python manage.py purge_mail_log 7 -v 0 00 0 * * * root cd /var/www/note_kfet && env/bin/python manage.py purge_mail_log 7 -v 0
00 0 * * * root cd /var/www/note_kfet && env/bin/python manage.py purge_mail_log -r failure 30 -v 0
# Faire une sauvegarde de la base de données # Faire une sauvegarde de la base de données
00 2 * * * root cd /var/www/note_kfet && apps/scripts/shell/backup_db 00 2 * * * root cd /var/www/note_kfet && apps/scripts/shell/backup_db
# Vérifier la cohérence de la base et mailer en cas de problème # Vérifier la cohérence de la base et mailer en cas de problème

View File

@ -225,6 +225,7 @@ MEDIA_URL = '/media/'
# Use mailer in production to place emails in a queue before sending them to avoid spam # Use mailer in production to place emails in a queue before sending them to avoid spam
EMAIL_BACKEND = 'mailer.backend.DbBackend' EMAIL_BACKEND = 'mailer.backend.DbBackend'
MAILER_EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' MAILER_EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
MAILER_EMAIL_MAX_BATCH = 10
EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', False) EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', False)
EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.example.org') EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.example.org')
EMAIL_PORT = os.getenv('EMAIL_PORT', 25) EMAIL_PORT = os.getenv('EMAIL_PORT', 25)
@ -265,11 +266,9 @@ OAUTH2_PROVIDER = {
'SCOPES_BACKEND_CLASS': 'permission.scopes.PermissionScopes', 'SCOPES_BACKEND_CLASS': 'permission.scopes.PermissionScopes',
'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator", 'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator",
'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14), 'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14),
'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0)
} }
# PKCE (fix a breaking change of django-oauth-toolkit 2.0.0)
PKCE_REQUIRED = False
# Take control on how widget templates are sourced # Take control on how widget templates are sourced
# See https://docs.djangoproject.com/en/2.2/ref/forms/renderers/#templatessetting # See https://docs.djangoproject.com/en/2.2/ref/forms/renderers/#templatessetting
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'

View File

@ -72,7 +72,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-cutlery"></i> {% trans 'Food' %}</a> <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-cutlery"></i> {% trans 'Food' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if user.is_authenticated and user|is_member:"Kfet" %} {% if user.is_authenticated %}
<li class="nav-item"> <li class="nav-item">
{% url 'note:transfer' as url %} {% url 'note:transfer' as url %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-exchange"></i> {% trans 'Transfer' %}</a> <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-exchange"></i> {% trans 'Transfer' %}</a>

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
</h3> </h3>
<div class="card-body"> <div class="card-body">
<input id="searchbar" type="text" class="form-control" <input id="searchbar" type="text" class="form-control"
placeholder="{% trans "Search by attribute such as name" %}"> placeholder="{% trans "Search by attribute such as name..." %}">
</div> </div>
<div id="dynamic-table"> <div id="dynamic-table">
{% if table.data %} {% if table.data %}

View File

@ -1,14 +1,14 @@
[tox] [tox]
envlist = envlist =
# Debian Bullseye Python
py39-django42
# Ubuntu 22.04 Python # Ubuntu 22.04 Python
py310-django42 py310-django42
# Debian Bookworm Python # Debian Bookworm Python
py311-django42 py311-django42
# Ubuntu 24.04 Python
py312-django42
linters linters
skipsdist = True skipsdist = True