1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-22 18:38:23 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
bbcecf1f6d Merge branch 'non-BDE-members' into 'main'
Draft: Allow non-BDE members to use the note

See merge request bde/nk20!268
2024-08-30 00:43:17 +02:00
13 changed files with 4426 additions and 4568 deletions

View File

@ -7,6 +7,21 @@ 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
@ -39,6 +54,8 @@ 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,11 +265,12 @@ 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 valid members # Keep only 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()).exclude(note__inactivity_reason='forced') note__noteuser__user__memberships__date_end__gte=timezone.now(),
)
# 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"))
@ -329,7 +330,7 @@ class ActivityEntryView(LoginRequiredMixin, SingleTableMixin, TemplateView):
context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk
context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk
activities_open = Activity.objects.filter(open=True, activity_type__manage_entries=True).filter( activities_open = Activity.objects.filter(open=True).filter(
PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all() PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all()
context["activities_open"] = [a for a in activities_open context["activities_open"] = [a for a in activities_open
if PermissionBackend.check_perm(self.request, if PermissionBackend.check_perm(self.request,

View File

@ -44,7 +44,6 @@ class ProfileForm(forms.ModelForm):
""" """
A form for the extras field provided by the :model:`member.Profile` model. A form for the extras field provided by the :model:`member.Profile` model.
""" """
# Remove widget=forms.HiddenInput() if you want to use report frequency.
report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency")) report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"))
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
@ -77,8 +76,7 @@ class ProfileForm(forms.ModelForm):
class Meta: class Meta:
model = Profile model = Profile
fields = '__all__' fields = '__all__'
# Remove ml_[asso]_registration from exclude if the concerned association uses nk20 to manage its mailing list. exclude = ('user', 'email_confirmed', 'registration_valid', )
exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', )
class ImageForm(forms.Form): class ImageForm(forms.Form):

View File

@ -26,7 +26,6 @@ from note_kfet.middlewares import _set_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from permission.models import Role from permission.models import Role
from permission.views import ProtectQuerysetMixin, ProtectedCreateView from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from django import forms
from .forms import UserForm, ProfileForm, ImageForm, ClubForm, MembershipForm, \ from .forms import UserForm, ProfileForm, ImageForm, ClubForm, MembershipForm, \
CustomAuthenticationForm, MembershipRolesForm CustomAuthenticationForm, MembershipRolesForm
@ -73,24 +72,11 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].required = True form.fields['email'].required = True
form.fields['email'].help_text = _("This address must be valid.") form.fields['email'].help_text = _("This address must be valid.")
profile_form = self.profile_form(instance=context['user_object'].profile, if PermissionBackend.check_perm(self.request, "member.change_profile", context['user_object'].profile):
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None) data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency: if not self.object.profile.report_frequency:
del profile_form.fields["last_report"] del context['profile_form'].fields["last_report"]
fields_to_check = list(profile_form.fields.keys())
fields_modifiable = False
# Delete the fields for which the user does not have the permission to modify
for field_name in fields_to_check:
if not PermissionBackend.check_perm(self.request, f"member.change_profile_{field_name}", context['user_object'].profile):
profile_form.fields[field_name].widget = forms.HiddenInput()
else:
fields_modifiable = True
if fields_modifiable:
context['profile_form'] = profile_form
return context return context

View File

@ -31,4 +31,3 @@ class RoleAdmin(admin.ModelAdmin):
Admin customisation for Role Admin customisation for Role
""" """
list_display = ('name', ) list_display = ('name', )
filter_horizontal = ('permissions',)

View File

@ -127,7 +127,7 @@
"auth", "auth",
"user" "user"
], ],
"query": "[\"AND\", {\"pk\": [\"user\", \"pk\"]}, {\"memberships__club__parent_club__isnull\": true}]", "query": "{\"pk\": [\"user\", \"pk\"]}",
"type": "change", "type": "change",
"mask": 1, "mask": 1,
"field": "last_login", "field": "last_login",
@ -3760,140 +3760,12 @@
"note", "note",
"alias" "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}]", "query": "[\"AND\", [\"OR\", {\"note__noteuser__user__memberships__club\": [\"club\"], \"note__noteuser__user__memberships__date_start__lte\": [\"today\"], \"note__noteuser__user__memberships__date_end__gte\": [\"today\"]}, {\"note__noteclub__isnull\": false}], {\"note__is_active\": true}]",
"type": "view", "type": "view",
"mask": 2, "mask": 1,
"field": "", "field": "",
"permanent": false, "permanent": false,
"description": "Voir les alias des notes des adhérent⋅es du club" "description": "Voir les alias des notes des clubs et 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.permission",
"pk": 242,
"fields": {
"model": [
"note",
"transaction"
],
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]",
"type": "add",
"mask": 2,
"field": "",
"permanent": false,
"description": "Créer une transaction vers la note d'un club"
}
},
{
"model": "permission.permission",
"pk": 243,
"fields": {
"model": [
"member",
"profile"
],
"query": "{\"user__memberships__club\": [\"club\"], \"user__memberships__date_start__lte\": [\"today\"],\"user__memberships__date_end__gte\": [\"today\"]}",
"type": "view",
"mask": 3,
"field": "",
"permanent": false,
"description": "Voir les profils des membres du club"
}
},
{
"model": "permission.permission",
"pk": 244,
"fields": {
"model": [
"member",
"profile"
],
"query": "{}",
"type": "change",
"mask": 3,
"field": "ml_events_registration",
"permanent": false,
"description": "Modifier l'abonnement à la Newsletter BDE pour n'importe quel profil"
}
},
{
"model": "permission.permission",
"pk": 245,
"fields": {
"model": [
"member",
"profile"
],
"query": "{}",
"type": "change",
"mask": 3,
"field": "ml_art_registration",
"permanent": false,
"description": "Modifier l'abonnement à la Newsletter Art pour n'importe quel profil"
}
},
{
"model": "permission.permission",
"pk": 246,
"fields": {
"model": [
"member",
"profile"
],
"query": "{}",
"type": "change",
"mask": 3,
"field": "ml_sport_registration",
"permanent": false,
"description": "Modifier l'abonnement à la Newsletter Sport pour n'importe quel profil"
}
},
{
"model": "permission.permission",
"pk": 247,
"fields": {
"model": [
"activity",
"guest"
],
"query": "{\"activity__organizer\": [\"club\"]}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir les personnes invitées aux événements organisés par son club"
} }
}, },
{ {
@ -4008,8 +3880,7 @@
12, 12,
13, 13,
14, 14,
22, 22
48
] ]
} }
}, },
@ -4058,7 +3929,6 @@
"for_club": null, "for_club": null,
"name": "Tr\u00e9sorièr\u22c5e de club", "name": "Tr\u00e9sorièr\u22c5e de club",
"permissions": [ "permissions": [
6,
19, 19,
20, 20,
21, 21,
@ -4073,9 +3943,7 @@
182, 182,
184, 184,
185, 185,
239, 239
240,
241
] ]
} }
}, },

View File

@ -276,22 +276,11 @@ 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.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 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[survey.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']
@ -302,7 +291,9 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
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, registration__first_year=False).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) # Remove hardcoded people
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
registration__information_json__icontains="hardcoded").count()
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
@ -317,6 +308,9 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
for bus in self.get_buses(): for bus in self.get_buses():
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).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)
# Remove hardcoded people
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
registration__information_json__icontains="hardcoded").count()
quotas[bus] = free_seats quotas[bus] = free_seats
if display_tqdm: if display_tqdm:

View File

@ -795,11 +795,11 @@ msgstr "Masque de permissions"
#: apps/member/forms.py:46 #: apps/member/forms.py:46
msgid "Report frequency" msgid "Report frequency"
msgstr "Fréquence des relevés (en jours)" msgstr "Fréquence des rapports (en jours)"
#: apps/member/forms.py:48 #: apps/member/forms.py:48
msgid "Last report date" msgid "Last report date"
msgstr "Date de dernier relevé" msgstr "Date de dernier rapport"
#: apps/member/forms.py:52 #: apps/member/forms.py:52
msgid "" msgid ""
@ -1032,7 +1032,6 @@ msgid ""
"Register on the mailing list to stay informed of the sport events of the " "Register on the mailing list to stay informed of the sport events of the "
"campus (1 mail/week)" "campus (1 mail/week)"
msgstr "" msgstr ""
"S'inscrire sur la liste de diffusion pour rester informé·e des actualités "
"sportives sur le campus (1 mail par semaine)" "sportives sur le campus (1 mail par semaine)"
#: apps/member/models.py:113 #: apps/member/models.py:113
@ -1045,11 +1044,11 @@ msgstr ""
#: apps/member/models.py:117 #: apps/member/models.py:117
msgid "report frequency (in days)" msgid "report frequency (in days)"
msgstr "fréquence des relevés (en jours)" msgstr "fréquence des rapports (en jours)"
#: apps/member/models.py:122 #: apps/member/models.py:122
msgid "last report date" msgid "last report date"
msgstr "date de dernier relevé" msgstr "date de dernier rapport"
#: apps/member/models.py:127 #: apps/member/models.py:127
msgid "email confirmed" msgid "email confirmed"

View File

@ -9,7 +9,6 @@ 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
@ -26,5 +25,3 @@ MAILTO=notekfet2020@lists.crans.org
00 9 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_highlighted_buttons -v 0 00 9 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_highlighted_buttons -v 0
# Vider les tokens Oauth2 # Vider les tokens Oauth2
00 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py cleartokens -v 0 00 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py cleartokens -v 0
# Envoyer la liste des abonnés à la NL BDA
00 8 * * 0 root cd /var/www/note_kfet && env/bin/python manage.py extract_ml_registrations -t art -v 0

View File

@ -225,7 +225,6 @@ 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)

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 %} {% if user.is_authenticated and user|is_member:"Kfet" %}
<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

@ -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