diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 33ce0cd8..4f041867 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,25 +7,25 @@ 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:
+# Debian Bullseye
+py39-django42:
stage: test
- image: ubuntu:20.04
+ 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
+py310-django42:
+ stage: test
+ 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 +37,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-django42
-# Debian Bullseye
-py39-django22:
+# Debian Bookworm
+py311-django42:
stage: test
- image: debian:bullseye
+ image: debian:bookworm
before_script:
- >
apt-get update &&
@@ -52,11 +52,13 @@ 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-django42
+
+
linters:
stage: quality-assurance
- image: debian:bullseye
+ image: debian:bookworm
before_script:
- apt-get update && apt-get install -y tox
script: tox -e linters
diff --git a/apps/activity/forms.py b/apps/activity/forms.py
index 718d4fe0..1070f19d 100644
--- a/apps/activity/forms.py
+++ b/apps/activity/forms.py
@@ -4,13 +4,14 @@
from datetime import timedelta
from random import shuffle
+from bootstrap_datepicker_plus.widgets import DateTimePickerInput
from django import forms
from django.contrib.contenttypes.models import ContentType
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..0659427f 100644
--- a/apps/api/urls.py
+++ b/apps/api/urls.py
@@ -2,7 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
-from django.conf.urls import url, include
+from django.conf.urls import include
+from django.urls import re_path
from rest_framework import routers
from .views import UserInformationView
@@ -47,7 +48,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 ad295b41..a74ddb90 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -3,7 +3,7 @@
import io
-from PIL import Image, ImageSequence
+from bootstrap_datepicker_plus.widgets import DatePickerInput
from django import forms
from django.conf import settings
from django.contrib.auth.forms import AuthenticationForm
@@ -13,8 +13,9 @@ 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 PIL import Image, ImageSequence
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/note/api/views.py b/apps/note/api/views.py
index ee1479be..5acda17c 100644
--- a/apps/note/api/views.py
+++ b/apps/note/api/views.py
@@ -183,19 +183,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..aa722b57 100644
--- a/apps/note/forms.py
+++ b/apps/note/forms.py
@@ -2,12 +2,13 @@
# 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/templates/note/amount_input.html b/apps/note/templates/note/amount_input.html
index d4873115..cbe9d160 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 is not False %}{{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}
{% endfor %}>
€
diff --git a/apps/permission/scopes.py b/apps/permission/scopes.py
index c3edfd93..af504b18 100644
--- a/apps/permission/scopes.py
+++ b/apps/permission/scopes.py
@@ -35,6 +35,8 @@ class PermissionScopes(BaseScopes):
class PermissionOAuth2Validator(OAuth2Validator):
+ oidc_claim_scope = None # fix breaking change of django-oauth-toolkit 2.0.0
+
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
"""
User can request as many scope as he wants, including invalid scopes,
diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py
index 94c1d5e8..c851248d 100644
--- a/apps/wei/forms/registration.py
+++ b/apps/wei/forms/registration.py
@@ -1,13 +1,14 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
+from bootstrap_datepicker_plus.widgets import DatePickerInput
from django import forms
from django.contrib.auth.models import User
from django.db.models import Q
from django.forms import CheckboxSelectMultiple
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial, NoteUser
-from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget
+from note_kfet.inputs import AmountInput, Autocomplete, ColorWidget
from ..models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole
diff --git a/apps/wei/tests/test_wei_registration.py b/apps/wei/tests/test_wei_registration.py
index 4422b324..2470b48d 100644
--- a/apps/wei/tests/test_wei_registration.py
+++ b/apps/wei/tests/test_wei_registration.py
@@ -439,7 +439,7 @@ class TestWEIRegistration(TestCase):
emergency_contact_phone='+33123456789',
))
self.assertEqual(response.status_code, 200)
- self.assertTrue("This user can't be in her/his first year since he/she has already participated to a WEI."
+ self.assertTrue("This user can't be in her/his first year since he/she has already participated to a WEI."
in str(response.context["form"].errors))
# Check that if the WEI is started, we can't register anyone
@@ -635,7 +635,7 @@ class TestWEIRegistration(TestCase):
))
self.assertEqual(response.status_code, 200)
self.assertFalse(response.context["form"].is_valid())
- self.assertTrue("This team doesn't belong to the given bus." in str(response.context["form"].errors))
+ self.assertTrue("This team doesn't belong to the given bus." in str(response.context["form"].errors))
response = self.client.post(reverse("wei:validate_registration", kwargs=dict(pk=self.registration.pk)), dict(
roles=[WEIRole.objects.get(name="GC WEI").id],
diff --git a/note_kfet/admin.py b/note_kfet/admin.py
index 0900c3b0..8b47de06 100644
--- a/note_kfet/admin.py
+++ b/note_kfet/admin.py
@@ -25,8 +25,8 @@ admin_site.register(Site, SiteAdmin)
# Add external apps model
if "oauth2_provider" in settings.INSTALLED_APPS:
- from oauth2_provider.admin import Application, ApplicationAdmin, Grant, \
- GrantAdmin, AccessToken, AccessTokenAdmin, RefreshToken, RefreshTokenAdmin
+ from oauth2_provider.admin import ApplicationAdmin, GrantAdmin, AccessTokenAdmin, RefreshTokenAdmin
+ from oauth2_provider.models import Application, Grant, AccessToken, RefreshToken
admin_site.register(Application, ApplicationAdmin)
admin_site.register(Grant, GrantAdmin)
admin_site.register(AccessToken, AccessTokenAdmin)
diff --git a/note_kfet/inputs.py b/note_kfet/inputs.py
index 0ad0797a..b68adf4d 100644
--- a/note_kfet/inputs.py
+++ b/note_kfet/inputs.py
@@ -68,264 +68,3 @@ class ColorWidget(Widget):
def value_from_datadict(self, data, files, name):
val = super().value_from_datadict(data, files, name)
return int(val[1:], 16)
-
-
-"""
-The remaining of this file comes from the project `django-bootstrap-datepicker-plus` available on Github:
-https://github.com/monim67/django-bootstrap-datepicker-plus
-This is distributed under Apache License 2.0.
-
-This adds datetime pickers with bootstrap.
-"""
-
-"""Contains Base Date-Picker input class for widgets of this package."""
-
-
-class DatePickerDictionary:
- """Keeps track of all date-picker input classes."""
-
- _i = 0
- items = dict()
-
- @classmethod
- def generate_id(cls):
- """Return a unique ID for each date-picker input class."""
- cls._i += 1
- return 'dp_%s' % cls._i
-
-
-class BasePickerInput(DateTimeBaseInput):
- """Base Date-Picker input class for widgets of this package."""
-
- template_name = 'bootstrap_datepicker_plus/date-picker.html'
- picker_type = 'DATE'
- format = '%Y-%m-%d'
- config = {}
- _default_config = {
- 'id': None,
- 'picker_type': None,
- 'linked_to': None,
- 'options': {} # final merged options
- }
- options = {} # options extended by user
- options_param = {} # options passed as parameter
- _default_options = {
- 'showClose': True,
- 'showClear': True,
- 'showTodayButton': True,
- "locale": "fr",
- }
-
- # source: https://github.com/tutorcruncher/django-bootstrap3-datetimepicker
- # file: /blob/31fbb09/bootstrap3_datetime/widgets.py#L33
- format_map = (
- ('DDD', r'%j'),
- ('DD', r'%d'),
- ('MMMM', r'%B'),
- ('MMM', r'%b'),
- ('MM', r'%m'),
- ('YYYY', r'%Y'),
- ('YY', r'%y'),
- ('HH', r'%H'),
- ('hh', r'%I'),
- ('mm', r'%M'),
- ('ss', r'%S'),
- ('a', r'%p'),
- ('ZZ', r'%z'),
- )
-
- class Media:
- """JS/CSS resources needed to render the date-picker calendar."""
-
- js = (
- 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/'
- 'moment-with-locales.min.js',
- 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
- '4.17.47/js/bootstrap-datetimepicker.min.js',
- 'bootstrap_datepicker_plus/js/datepicker-widget.js'
- )
- css = {'all': (
- 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
- '4.17.47/css/bootstrap-datetimepicker.css',
- 'bootstrap_datepicker_plus/css/datepicker-widget.css'
- ), }
-
- @classmethod
- def format_py2js(cls, datetime_format):
- """Convert python datetime format to moment datetime format."""
- for js_format, py_format in cls.format_map:
- datetime_format = datetime_format.replace(py_format, js_format)
- return datetime_format
-
- @classmethod
- def format_js2py(cls, datetime_format):
- """Convert moment datetime format to python datetime format."""
- for js_format, py_format in cls.format_map:
- datetime_format = datetime_format.replace(js_format, py_format)
- return datetime_format
-
- def __init__(self, attrs=None, format=None, options=None):
- """Initialize the Date-picker widget."""
- self.format_param = format
- self.options_param = options if options else {}
- self.config = self._default_config.copy()
- self.config['id'] = DatePickerDictionary.generate_id()
- self.config['picker_type'] = self.picker_type
- self.config['options'] = self._calculate_options()
- attrs = attrs if attrs else {}
- if 'class' not in attrs:
- attrs['class'] = 'form-control'
- super().__init__(attrs, self._calculate_format())
-
- def _calculate_options(self):
- """Calculate and Return the options."""
- _options = self._default_options.copy()
- _options.update(self.options)
- if self.options_param:
- _options.update(self.options_param)
- return _options
-
- def _calculate_format(self):
- """Calculate and Return the datetime format."""
- _format = self.format_param if self.format_param else self.format
- if self.config['options'].get('format'):
- _format = self.format_js2py(self.config['options'].get('format'))
- else:
- self.config['options']['format'] = self.format_py2js(_format)
- return _format
-
- def get_context(self, name, value, attrs):
- """Return widget context dictionary."""
- context = super().get_context(
- name, value, attrs)
- context['widget']['attrs']['dp_config'] = json_dumps(self.config)
- return context
-
- def start_of(self, event_id):
- """
- Set Date-Picker as the start-date of a date-range.
-
- Args:
- - event_id (string): User-defined unique id for linking two fields
- """
- DatePickerDictionary.items[str(event_id)] = self
- return self
-
- def end_of(self, event_id, import_options=True):
- """
- Set Date-Picker as the end-date of a date-range.
-
- Args:
- - event_id (string): User-defined unique id for linking two fields
- - import_options (bool): inherit options from start-date input,
- default: TRUE
- """
- event_id = str(event_id)
- if event_id in DatePickerDictionary.items:
- linked_picker = DatePickerDictionary.items[event_id]
- self.config['linked_to'] = linked_picker.config['id']
- if import_options:
- backup_moment_format = self.config['options']['format']
- self.config['options'].update(linked_picker.config['options'])
- self.config['options'].update(self.options_param)
- if self.format_param or 'format' in self.options_param:
- self.config['options']['format'] = backup_moment_format
- else:
- self.format = linked_picker.format
- # Setting useCurrent is necessary, see following issue
- # https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075
- self.config['options']['useCurrent'] = False
- self._link_to(linked_picker)
- else:
- raise KeyError(
- 'start-date not specified for event_id "%s"' % event_id)
- return self
-
- def _link_to(self, linked_picker):
- """
- Executed when two date-inputs are linked together.
-
- This method for sub-classes to override to customize the linking.
- """
- pass
-
-
-class DatePickerInput(BasePickerInput):
- """
- Widget to display a Date-Picker Calendar on a DateField property.
-
- Args:
- - attrs (dict): HTML attributes of rendered HTML input
- - format (string): Python DateTime format eg. "%Y-%m-%d"
- - options (dict): Options to customize the widget, see README
- """
-
- picker_type = 'DATE'
- format = '%Y-%m-%d'
- format_key = 'DATE_INPUT_FORMATS'
-
-
-class TimePickerInput(BasePickerInput):
- """
- Widget to display a Time-Picker Calendar on a TimeField property.
-
- Args:
- - attrs (dict): HTML attributes of rendered HTML input
- - format (string): Python DateTime format eg. "%Y-%m-%d"
- - options (dict): Options to customize the widget, see README
- """
-
- picker_type = 'TIME'
- format = '%H:%M'
- format_key = 'TIME_INPUT_FORMATS'
- template_name = 'bootstrap_datepicker_plus/time_picker.html'
-
-
-class DateTimePickerInput(BasePickerInput):
- """
- Widget to display a DateTime-Picker Calendar on a DateTimeField property.
-
- Args:
- - attrs (dict): HTML attributes of rendered HTML input
- - format (string): Python DateTime format eg. "%Y-%m-%d"
- - options (dict): Options to customize the widget, see README
- """
-
- picker_type = 'DATETIME'
- format = '%Y-%m-%d %H:%M'
- format_key = 'DATETIME_INPUT_FORMATS'
-
-
-class MonthPickerInput(BasePickerInput):
- """
- Widget to display a Month-Picker Calendar on a DateField property.
-
- Args:
- - attrs (dict): HTML attributes of rendered HTML input
- - format (string): Python DateTime format eg. "%Y-%m-%d"
- - options (dict): Options to customize the widget, see README
- """
-
- picker_type = 'MONTH'
- format = '01/%m/%Y'
- format_key = 'DATE_INPUT_FORMATS'
-
-
-class YearPickerInput(BasePickerInput):
- """
- Widget to display a Year-Picker Calendar on a DateField property.
-
- Args:
- - attrs (dict): HTML attributes of rendered HTML input
- - format (string): Python DateTime format eg. "%Y-%m-%d"
- - options (dict): Options to customize the widget, see README
- """
-
- picker_type = 'YEAR'
- format = '01/01/%Y'
- format_key = 'DATE_INPUT_FORMATS'
-
- def _link_to(self, linked_picker):
- """Customize the options when linked with other date-time input"""
- yformat = self.config['options']['format'].replace('-01-01', '-12-31')
- self.config['options']['format'] = yformat
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 74fed818..168f34bd 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -40,8 +40,9 @@ INSTALLED_APPS = [
# External apps
'bootstrap_datepicker_plus',
'colorfield',
+ 'crispy_bootstrap4',
'crispy_forms',
- 'django_htcpcp_tea',
+# 'django_htcpcp_tea',
'django_tables2',
'mailer',
'phonenumber_field',
@@ -90,12 +91,14 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
- 'django_htcpcp_tea.middleware.HTCPCPTeaMiddleware',
'note_kfet.middlewares.SessionMiddleware',
'note_kfet.middlewares.LoginByIPMiddleware',
'note_kfet.middlewares.TurbolinksMiddleware',
'note_kfet.middlewares.ClacksMiddleware',
]
+if "django_htcpcp_tea" in INSTALLED_APPS:
+ MIDDLEWARE.append('django_htcpcp_tea.middleware.HTCPCPTeaMiddleware')
+
ROOT_URLCONF = 'note_kfet.urls'
@@ -263,6 +266,9 @@ OAUTH2_PROVIDER = {
'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14),
}
+# PKCE (fix a breaking change of django-oauth-toolkit 2.0.0)
+PKCE_REQUIRED = False
+
# Take control on how widget templates are sourced
# See https://docs.djangoproject.com/en/2.2/ref/forms/renderers/#templatessetting
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
@@ -274,6 +280,7 @@ LOGIN_REDIRECT_URL = '/'
SESSION_COOKIE_AGE = 60 * 60 * 3
# Use Crispy Bootstrap4 theme
+CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap4'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
# Use Django Table2 Bootstrap4 theme
@@ -295,3 +302,6 @@ PHONENUMBER_DEFAULT_REGION = 'FR'
# We add custom information to CAS, in order to give a normalized name to other services
CAS_AUTH_CLASS = 'member.auth.CustomAuthUser'
+
+# Default field for primary key
+DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
diff --git a/note_kfet/templates/autocomplete_model.html b/note_kfet/templates/autocomplete_model.html
index fa24213f..5ffe971d 100644
--- a/note_kfet/templates/autocomplete_model.html
+++ b/note_kfet/templates/autocomplete_model.html
@@ -8,7 +8,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if widget.value != None and widget.value != "" %}value="{{ widget.value }}"{% endif %}
name="{{ widget.name }}_name" autocomplete="off"
{% for name, value in widget.attrs.items %}
- {% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
+ {% if value is not False %}{{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}
{% endfor %}
aria-describedby="{{widget.attrs.id}}_tooltip">
{% if widget.resetable %}
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index d222c239..9008a16d 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -30,9 +30,6 @@ urlpatterns = [
path('accounts/', include('django.contrib.auth.urls')),
path('api/', include('api.urls')),
path('permission/', include('permission.urls')),
-
- # Make coffee
- path('coffee/', include('django_htcpcp_tea.urls')),
]
# During development, serve static and media files
@@ -57,6 +54,11 @@ if "debug_toolbar" in settings.INSTALLED_APPS:
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
+if "django_htcpcp_tea" in settings.INSTALLED_APPS:
+ # Make coffee
+ urlpatterns.append(
+ path('coffee/', include('django_htcpcp_tea.urls'))
+ )
handler400 = bad_request
handler403 = permission_denied
diff --git a/requirements.txt b/requirements.txt
index f4ece220..f4a32c97 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,19 +1,20 @@
-beautifulsoup4~=4.7.1
-Django~=2.2.15
-django-bootstrap-datepicker-plus~=3.0.5
-django-cas-server~=1.2.0
-django-colorfield~=0.3.2
-django-crispy-forms~=1.7.2
-django-extensions>=2.1.4
-django-filter~=2.1
-django-htcpcp-tea~=0.3.1
-django-mailer~=2.0.1
-django-oauth-toolkit~=1.3.3
-django-phonenumber-field~=5.0.0
-django-polymorphic>=2.0.3,<3.0.0
-djangorestframework>=3.9.0,<3.13.0
-django-rest-polymorphic~=0.1.9
-django-tables2~=2.3.1
-python-memcached~=1.59
-phonenumbers~=8.9.10
-Pillow>=5.4.1
+beautifulsoup4~=4.12.3
+crispy-bootstrap4~=2023.1
+Django~=4.2.9
+django-bootstrap-datepicker-plus~=5.0.5
+#django-cas-server~=2.0.0
+django-colorfield~=0.11.0
+django-crispy-forms~=2.1.0
+django-extensions>=3.2.3
+django-filter~=23.5
+#django-htcpcp-tea~=0.8.1
+django-mailer~=2.3.1
+django-oauth-toolkit~=2.3.0
+django-phonenumber-field~=7.3.0
+django-polymorphic~=3.1.0
+djangorestframework~=3.14.0
+django-rest-polymorphic~=0.1.10
+django-tables2~=2.7.0
+python-memcached~=1.62
+phonenumbers~=8.13.28
+Pillow>=10.2.0
diff --git a/tox.ini b/tox.ini
index 8039e22f..31d827a0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,13 +1,13 @@
[tox]
envlist =
- # Debian Buster Python
- py37-django22
-
- # Ubuntu 20.04 Python
- py38-django22
-
# Debian Bullseye Python
- py39-django22
+ py39-django42
+
+ # Ubuntu 22.04 Python
+ py310-django42
+
+ # Debian Bookworm Python
+ py311-django42
linters
skipsdist = True