diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 33ce0cd8..8e3ec20a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,25 +7,10 @@ stages: variables: GIT_SUBMODULE_STRATEGY: recursive -# Debian Buster -# py37-django22: -# stage: test -# image: debian:buster-backports -# before_script: -# - > -# apt-get update && -# apt-get install --no-install-recommends -t buster-backports -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 py37-django22 - -# Ubuntu 20.04 -py38-django22: +# Ubuntu 22.04 +py310-django50: stage: test - image: ubuntu:20.04 + image: ubuntu:22.04 before_script: # Fix tzdata prompt - ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo Europe/Paris > /etc/timezone @@ -37,12 +22,12 @@ py38-django22: 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 py38-django22 + script: tox -e py310-django50 -# Debian Bullseye -py39-django22: +# Debian Bookworm +py311-django50: stage: test - image: debian:bullseye + image: debian:bookworm before_script: - > apt-get update && @@ -52,7 +37,7 @@ py39-django22: 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-django22 + script: tox -e py311-django50 linters: stage: quality-assurance diff --git a/apps/activity/forms.py b/apps/activity/forms.py index 6e1c35ff..c7733d8c 100644 --- a/apps/activity/forms.py +++ b/apps/activity/forms.py @@ -1,6 +1,8 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from bootstrap_datepicker_plus.widgets import DateTimePickerInput + from datetime import timedelta from random import shuffle @@ -10,7 +12,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from member.models import Club from note.models import Note, NoteUser -from note_kfet.inputs import Autocomplete, DateTimePickerInput +from note_kfet.inputs import Autocomplete from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend diff --git a/apps/api/urls.py b/apps/api/urls.py index 7c73093b..d328ae97 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings -from django.conf.urls import url, include +from django.urls import include, re_path from rest_framework import routers from .views import UserInformationView @@ -47,7 +47,7 @@ app_name = 'api' # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ - url('^', include(router.urls)), - url('^me/', UserInformationView.as_view()), - url('^api-auth/', include('rest_framework.urls', namespace='rest_framework')), + re_path('^', include(router.urls)), + re_path('^me/', UserInformationView.as_view()), + re_path('^api-auth/', include('rest_framework.urls', namespace='rest_framework')), ] diff --git a/apps/member/forms.py b/apps/member/forms.py index 3d892705..bd5d4065 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -3,6 +3,7 @@ import io +from bootstrap_datepicker_plus.widgets import DatePickerInput from PIL import Image, ImageSequence from django import forms from django.conf import settings @@ -13,7 +14,7 @@ from django.forms import CheckboxSelectMultiple from django.utils import timezone from django.utils.translation import gettext_lazy as _ from note.models import NoteSpecial, Alias -from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput +from note_kfet.inputs import Autocomplete, AmountInput from permission.models import PermissionMask, Role from .models import Profile, Club, Membership @@ -32,7 +33,7 @@ class UserForm(forms.ModelForm): # Django usernames can only contain letters, numbers, @, ., +, - and _. # We want to allow users to have uncommon and unpractical usernames: # That is their problem, and we have normalized aliases for us. - return super()._get_validation_exclusions() + ["username"] + return super()._get_validation_exclusions() | {"username"} class Meta: model = User diff --git a/apps/member/tests/test_login.py b/apps/member/tests/test_login.py index b8873a14..ce5de1cf 100644 --- a/apps/member/tests/test_login.py +++ b/apps/member/tests/test_login.py @@ -44,7 +44,7 @@ class TemplateLoggedInTests(TestCase): self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, 302, 302) def test_logout(self): - response = self.client.get(reverse("logout")) + response = self.client.post(reverse("logout")) self.assertEqual(response.status_code, 200) def test_admin_index(self): diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py index 0522ebea..349348da 100644 --- a/apps/note/api/urls.py +++ b/apps/note/api/urls.py @@ -13,7 +13,7 @@ def register_note_urls(router, path): router.register(path + '/note', NotePolymorphicViewSet) router.register(path + '/alias', AliasViewSet) router.register(path + '/trust', TrustViewSet) - router.register(path + '/consumer', ConsumerViewSet) + router.register(path + '/consumer', ConsumerViewSet, basename="consumer") router.register(path + '/transaction/category', TemplateCategoryViewSet) router.register(path + '/transaction/transaction', TransactionViewSet) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 0aba9adc..c70d3e97 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -179,19 +179,10 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): # We match first an alias if it is matched without normalization, # then if the normalized pattern matches a normalized alias. queryset = queryset.filter( - **{f'name{suffix}': alias_prefix + alias} - ).union( - queryset.filter( - Q(**{f'normalized_name{suffix}': alias_prefix + Alias.normalize(alias)}) - & ~Q(**{f'name{suffix}': alias_prefix + alias}) - ), - all=True).union( - queryset.filter( - Q(**{f'normalized_name{suffix}': alias_prefix + alias.lower()}) - & ~Q(**{f'normalized_name{suffix}': alias_prefix + Alias.normalize(alias)}) - & ~Q(**{f'name{suffix}': alias_prefix + alias}) - ), - all=True) + Q(**{f'name{suffix}': alias_prefix + alias}) + | Q(**{f'normalized_name{suffix}': alias_prefix + Alias.normalize(alias)}) + | Q(**{f'normalized_name{suffix}': alias_prefix + alias.lower()}) + ) queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ else queryset.order_by("name") diff --git a/apps/note/forms.py b/apps/note/forms.py index 209b49d9..92e17569 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -1,13 +1,15 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + from datetime import datetime +from bootstrap_datepicker_plus.widgets import DateTimePickerInput from django import forms from django.contrib.contenttypes.models import ContentType from django.forms import CheckboxSelectMultiple from django.utils.timezone import make_aware from django.utils.translation import gettext_lazy as _ -from note_kfet.inputs import Autocomplete, AmountInput, DateTimePickerInput +from note_kfet.inputs import Autocomplete, AmountInput from .models import TransactionTemplate, NoteClub, Alias diff --git a/apps/note/migrations/0002_create_special_notes.py b/apps/note/migrations/0002_create_special_notes.py index 12fa8583..07935d54 100644 --- a/apps/note/migrations/0002_create_special_notes.py +++ b/apps/note/migrations/0002_create_special_notes.py @@ -18,6 +18,7 @@ def create_special_notes(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ ('note', '0001_initial'), + ('logs', '0001_initial'), ] operations = [ diff --git a/apps/note/migrations/0007_alter_note_polymorphic_ctype_and_more.py b/apps/note/migrations/0007_alter_note_polymorphic_ctype_and_more.py new file mode 100644 index 00000000..171263a2 --- /dev/null +++ b/apps/note/migrations/0007_alter_note_polymorphic_ctype_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.7 on 2024-07-11 09:24 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('note', '0006_trust'), + ] + + operations = [ + migrations.AlterField( + model_name='note', + name='polymorphic_ctype', + field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'), + ), + migrations.AlterField( + model_name='transaction', + name='polymorphic_ctype', + field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'), + ), + ] diff --git a/apps/note/templates/note/amount_input.html b/apps/note/templates/note/amount_input.html index d4873115..0f18adb7 100644 --- a/apps/note/templates/note/amount_input.html +++ b/apps/note/templates/note/amount_input.html @@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-3.0-or-later name="{{ widget.name }}" {# Other attributes are loaded #} {% for name, value in widget.attrs.items %} - {% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %} + {% if value != False %}{{ name }}{% if value != True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %} {% endfor %}>