Compare commits

...
7 Commits
Author SHA1 Message Date
klmp200 35a55ff95a Fix animation bug on club list
Also, due to some weird jinja bug, css block is applied even if supposedly
in a conditional statement
2026-09-08 23:17:35 +02:00
klmp200 00cae33a33 Fix moderation view 2026-09-07 17:36:53 +02:00
klmp200 4bfcb6a267 Fix club-list pagination 2026-09-07 17:36:50 +02:00
thomas girod 4f885b4ba7 Merge pull request #1479 from ae-utbm/bump-python
python 3.14
2026-09-07 17:03:57 +02:00
imperosol 13ee70afe9 update python deps 2026-09-07 16:16:55 +02:00
imperosol 7bd79d1d55 re-apply ruff TCH rules 2026-09-07 13:32:13 +02:00
imperosol d93afffbc3 python 3.14 2026-09-07 11:48:02 +02:00
69 changed files with 615 additions and 755 deletions
+1 -1
View File
@@ -1 +1 @@
3.12 3.14
+6 -2
View File
@@ -1,11 +1,15 @@
from typing import TYPE_CHECKING
from django.contrib import admin, messages from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from api.hashers import generate_key from api.hashers import generate_key
from api.models import ApiClient, ApiKey from api.models import ApiClient, ApiKey
if TYPE_CHECKING:
from django.db.models import QuerySet
from django.http import HttpRequest
@admin.register(ApiClient) @admin.register(ApiClient)
class ApiClientAdmin(admin.ModelAdmin): class ApiClientAdmin(admin.ModelAdmin):
+5 -1
View File
@@ -1,9 +1,13 @@
from django.http import HttpRequest from typing import TYPE_CHECKING
from ninja.security import APIKeyHeader from ninja.security import APIKeyHeader
from api.hashers import get_hasher from api.hashers import get_hasher
from api.models import ApiClient, ApiKey from api.models import ApiClient, ApiKey
if TYPE_CHECKING:
from django.http import HttpRequest
class ApiKeyAuth(APIKeyHeader): class ApiKeyAuth(APIKeyHeader):
"""Authentication through client api keys.""" """Authentication through client api keys."""
+6 -4
View File
@@ -39,15 +39,17 @@ Example:
import operator import operator
from functools import reduce from functools import reduce
from typing import Any, Callable from typing import TYPE_CHECKING, Any, Callable
from django.contrib.auth.models import Permission
from django.http import HttpRequest
from ninja_extra import ControllerBase
from ninja_extra.permissions import BasePermission from ninja_extra.permissions import BasePermission
from counter.utils import is_logged_in_counter from counter.utils import is_logged_in_counter
if TYPE_CHECKING:
from django.contrib.auth.models import Permission
from django.http import HttpRequest
from ninja_extra import ControllerBase
class IsInGroup(BasePermission): class IsInGroup(BasePermission):
"""Check that the user is in the group whose primary key is given.""" """Check that the user is in the group whose primary key is given."""
+6 -2
View File
@@ -12,12 +12,16 @@
# OR WITHIN THE LOCAL FILE "LICENSE" # OR WITHIN THE LOCAL FILE "LICENSE"
# #
# #
from typing import TYPE_CHECKING
from django.contrib import admin from django.contrib import admin
from django.forms.models import ModelForm
from django.http import HttpRequest
from club.models import Club, ClubLink, ClubRole, LinkType, Membership from club.models import Club, ClubLink, ClubRole, LinkType, Membership
if TYPE_CHECKING:
from django.forms.models import ModelForm
from django.http import HttpRequest
@admin.register(Club) @admin.register(Club)
class ClubAdmin(admin.ModelAdmin): class ClubAdmin(admin.ModelAdmin):
@@ -1,12 +1,16 @@
# Generated by Django 4.2.16 on 2024-11-20 17:08 # Generated by Django 4.2.16 on 2024-11-20 17:08
from typing import TYPE_CHECKING
import django.db.models.deletion import django.db.models.deletion
import django.db.models.functions.datetime import django.db.models.functions.datetime
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Q from django.db.models import Q
from django.utils.timezone import localdate from django.utils.timezone import localdate
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
# Before the club role rework, the maximum free role # Before the club role rework, the maximum free role
# was the hardcoded highest non-board role # was the hardcoded highest non-board role
MAXIMUM_FREE_ROLE = 1 MAXIMUM_FREE_ROLE = 1
@@ -1,10 +1,14 @@
# Generated by Django 5.2.3 on 2025-06-21 21:59 # Generated by Django 5.2.3 on 2025-06-21 21:59
from typing import TYPE_CHECKING
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Case, When from django.db.models import Case, When
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
PRESIDENCY_ROLES = [10, 9] PRESIDENCY_ROLES = [10, 9]
MAXIMUM_FREE_ROLE = 1 MAXIMUM_FREE_ROLE = 1
SITH_CLUB_ROLES = { SITH_CLUB_ROLES = {
+11 -6
View File
@@ -15,9 +15,6 @@
{% endblock %} {% endblock %}
{% else %} {% else %}
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% block additional_css %}
<link rel="stylesheet" href="{{ static("club/list.scss") }}">
{% endblock %}
{% block description -%} {% block description -%}
{% trans %}The list of all clubs existing at UTBM.{% endtrans %} {% trans %}The list of all clubs existing at UTBM.{% endtrans %}
{%- endblock %} {%- endblock %}
@@ -26,6 +23,12 @@
{%- endblock %} {%- endblock %}
{% endif %} {% endif %}
{% block additional_css %}
{% if not is_fragment %}
<link rel="stylesheet" href="{{ static("club/list.scss") }}">
{% endif %}
{% endblock %}
{% from "core/macros.jinja" import paginate_htmx %} {% from "core/macros.jinja" import paginate_htmx %}
{% block content %} {% block content %}
@@ -33,15 +36,17 @@
<h3>{% trans %}Filters{% endtrans %}</h3> <h3>{% trans %}Filters{% endtrans %}</h3>
<form <form
id="club-list-filters" id="club-list-filters"
hx-get="{{ url("club:club_list") }}" method="GET"
hx-action="{{ url("club:club_list") }}"
hx-target="#content" hx-target="#content"
hx-swap="outerHtml" hx-swap="innerHTML"
hx-push-url="true" hx-push-url="true"
hx-disable="find input, find button"
> >
<div class="row gap-4x"> <div class="row gap-4x">
{{ form }} {{ form }}
</div> </div>
<button type="submit" class="btn btn-blue margin-bottom"> <button class="btn btn-blue margin-bottom">
<i class="fa fa-magnifying-glass"></i>{% trans %}Search{% endtrans %} <i class="fa fa-magnifying-glass"></i>{% trans %}Search{% endtrans %}
</button> </button>
</form> </form>
+4 -1
View File
@@ -1,9 +1,9 @@
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.db import ProgrammingError from django.db import ProgrammingError
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import localdate from django.utils.timezone import localdate
from model_bakery import baker from model_bakery import baker
@@ -14,6 +14,9 @@ from club.models import Club, ClubRole, Membership
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
from core.models import User from core.models import User
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_club_queryset_having_board_member(): def test_club_queryset_having_board_member():
+4 -1
View File
@@ -1,4 +1,4 @@
from collections.abc import Callable from typing import TYPE_CHECKING
import pytest import pytest
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@@ -14,6 +14,9 @@ from club.models import Club, ClubRole, Membership
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
from core.models import AnonymousUser, User from core.models import AnonymousUser, User
if TYPE_CHECKING:
from collections.abc import Callable
def make_club(): def make_club():
# unittest-style tests cannot use fixture, so we create a function # unittest-style tests cannot use fixture, so we create a function
+5 -1
View File
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING
import pytest import pytest
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from pytest_django.asserts import assertRedirects from pytest_django.asserts import assertRedirects
@@ -7,6 +8,9 @@ from pytest_django.asserts import assertRedirects
from club.models import Club, ClubRole, Membership from club.models import Club, ClubRole, Membership
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_club_board_member_cannot_edit_club_properties(client: Client): def test_club_board_member_cannot_edit_club_properties(client: Client):
+4 -1
View File
@@ -1,6 +1,6 @@
import itertools import itertools
from collections.abc import Callable
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
import pytest import pytest
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@@ -19,6 +19,9 @@ from club.tests.base import TestClub
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
from core.models import AnonymousUser, User from core.models import AnonymousUser, User
if TYPE_CHECKING:
from collections.abc import Callable
class TestMembershipQuerySet(TestClub): class TestMembershipQuerySet(TestClub):
def test_ongoing(self): def test_ongoing(self):
+5 -1
View File
@@ -1,7 +1,8 @@
from typing import TYPE_CHECKING
import pytest import pytest
from aemark import markdown from aemark import markdown
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from pytest_django.asserts import assertHTMLEqual, assertRedirects from pytest_django.asserts import assertHTMLEqual, assertRedirects
@@ -10,6 +11,9 @@ from club.models import Club, ClubRole, Membership
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
from core.models import PageRev, User from core.models import PageRev, User
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_page_display_on_club_main_page(client: Client): def test_page_display_on_club_main_page(client: Client):
+5 -1
View File
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING
import pytest import pytest
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
@@ -7,6 +8,9 @@ from club.models import Club
from com.models import Poster from com.models import Poster
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.parametrize("route_url", ["club:poster_list", "club:poster_create"]) @pytest.mark.parametrize("route_url", ["club:poster_list", "club:poster_create"])
+4 -1
View File
@@ -1,8 +1,8 @@
import csv import csv
import itertools import itertools
from typing import TYPE_CHECKING
import pytest import pytest
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
@@ -12,6 +12,9 @@ from core.models import User
from counter.baker_recipes import product_recipe, sale_recipe from counter.baker_recipes import product_recipe, sale_recipe
from counter.models import Counter, Customer, Product, Selling from counter.models import Counter, Customer, Product, Selling
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_sales_page_doesnt_crash(client: Client): def test_sales_page_doesnt_crash(client: Client):
+4 -1
View File
@@ -1,4 +1,5 @@
from datetime import date from datetime import date
from typing import TYPE_CHECKING
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django import forms from django import forms
@@ -9,11 +10,13 @@ from django.utils.translation import gettext_lazy as _
from club.models import Club from club.models import Club
from club.widgets.ajax_select import AutoCompleteSelectClub from club.widgets.ajax_select import AutoCompleteSelectClub
from com.models import News, NewsDate, Poster from com.models import News, NewsDate, Poster
from core.models import User
from core.utils import get_end_of_semester from core.utils import get_end_of_semester
from core.views.forms import SelectDateTime from core.views.forms import SelectDateTime
from core.views.widgets.markdown import MarkdownInput from core.views.widgets.markdown import MarkdownInput
if TYPE_CHECKING:
from core.models import User
class PosterForm(forms.ModelForm): class PosterForm(forms.ModelForm):
class Meta: class Meta:
+8 -3
View File
@@ -1,11 +1,10 @@
from pathlib import Path from typing import TYPE_CHECKING
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.contrib.syndication.views import add_domain from django.contrib.syndication.views import add_domain
from django.db.models import Count, OuterRef, QuerySet, Subquery from django.db.models import Count, OuterRef, QuerySet, Subquery
from django.http import HttpRequest
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from ical.calendar import Calendar from ical.calendar import Calendar
@@ -14,7 +13,13 @@ from ical.event import Event
from ical.types import Frequency, Recur from ical.types import Frequency, Recur
from com.models import News, NewsDate from com.models import News, NewsDate
from core.models import User
if TYPE_CHECKING:
from pathlib import Path
from django.http import HttpRequest
from core.models import User
def as_absolute_url(url: str, request: HttpRequest | None = None) -> str: def as_absolute_url(url: str, request: HttpRequest | None = None) -> str:
+4 -1
View File
@@ -1,11 +1,11 @@
from datetime import timedelta from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING
import pytest import pytest
from aemark import markdown from aemark import markdown
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.http import HttpResponse
from django.test import Client, TestCase from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
@@ -16,6 +16,9 @@ from com.ics_calendar import IcsCalendar
from com.models import News, NewsDate from com.models import News, NewsDate
from core.models import User from core.models import User
if TYPE_CHECKING:
from django.http import HttpResponse
def accel_redirect_to_file(response: HttpResponse) -> Path | None: def accel_redirect_to_file(response: HttpResponse) -> Path | None:
redirect = Path(response.headers.get("X-Accel-Redirect", "")) redirect = Path(response.headers.get("X-Accel-Redirect", ""))
@@ -1,9 +1,13 @@
# Generated by Django 4.2.17 on 2025-01-04 16:42 # Generated by Django 4.2.17 on 2025-01-04 16:42
from typing import TYPE_CHECKING
from django.db import migrations from django.db import migrations
from django.db.migrations.state import StateApps
from django.db.models import F from django.db.models import F
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def invert_is_manually_manageable(apps: StateApps, schema_editor): def invert_is_manually_manageable(apps: StateApps, schema_editor):
"""Invert `is_manually_manageable`. """Invert `is_manually_manageable`.
@@ -1,10 +1,14 @@
# Generated by Django 4.2.17 on 2024-12-31 13:30 # Generated by Django 4.2.17 on 2024-12-31 13:30
from typing import TYPE_CHECKING
import django.contrib.auth.models import django.contrib.auth.models
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def migrate_ban_groups(apps: StateApps, schema_editor): def migrate_ban_groups(apps: StateApps, schema_editor):
@@ -1,11 +1,15 @@
# Generated by Django 5.2.12 on 2026-05-01 08:59 # Generated by Django 5.2.12 on 2026-05-01 08:59
from typing import TYPE_CHECKING
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import F from django.db.models import F
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def set_updated_at(apps: StateApps, schema_editor): def set_updated_at(apps: StateApps, schema_editor):
SithFile = apps.get_model("core", "SithFile") SithFile = apps.get_model("core", "SithFile")
+1
View File
@@ -62,6 +62,7 @@ document.body.addEventListener(
}, },
); );
htmx.config.transitions = true;
Object.assign(window, { htmx }); Object.assign(window, { htmx });
/** /**
@@ -31,7 +31,7 @@
{% if is_fragment %} {% if is_fragment %}
hx-post="{{ action }}" hx-post="{{ action }}"
hx-target="#content" hx-target="#content"
hx-swap="outerHtml" hx-swap="innerHTML"
{% endif %} {% endif %}
>{% trans %}Confirm{% endtrans %}</button> >{% trans %}Confirm{% endtrans %}</button>
@@ -39,7 +39,7 @@
{% if is_fragment %} {% if is_fragment %}
hx-get="{{ previous }}" hx-get="{{ previous }}"
hx-target="#content" hx-target="#content"
hx-swap="outerHtml" hx-swap="innerHTML"
{% else %} {% else %}
action="window.history.back()" action="window.history.back()"
{% endif %} {% endif %}
+2 -2
View File
@@ -39,13 +39,13 @@
<p><button <p><button
hx-get="{{ url('core:file_moderate', file_id=f.id) }}" hx-get="{{ url('core:file_moderate', file_id=f.id) }}"
hx-target="#content" hx-target="#content"
hx-swap="outerHtml" hx-swap="innerHTML"
>{% trans %}Moderate{% endtrans %}</button> - >{% trans %}Moderate{% endtrans %}</button> -
{% set current_page = url('core:file_moderation') + "?page=" + page_obj.number | string %} {% set current_page = url('core:file_moderation') + "?page=" + page_obj.number | string %}
<button <button
hx-get="{{ url('core:file_delete', file_id=f.id) }}?next={{ current_page | urlencode }}&previous={{ current_page | urlencode }}" hx-get="{{ url('core:file_delete', file_id=f.id) }}?next={{ current_page | urlencode }}&previous={{ current_page | urlencode }}"
hx-target="#file-{{ loop.index }}" hx-target="#file-{{ loop.index }}"
hx-swap="outerHtml" hx-swap="innerHTML"
>{% trans %}Delete{% endtrans %}</button></p> >{% trans %}Delete{% endtrans %}</button></p>
</div> </div>
{% endfor %} {% endfor %}
+4 -2
View File
@@ -20,13 +20,15 @@
# Place - Suite 330, Boston, MA 02111-1307, USA. # Place - Suite 330, Boston, MA 02111-1307, USA.
# #
# #
from typing import Callable from typing import TYPE_CHECKING, Callable
import honeypot.templatetags.honeypot as honeypot_filters import honeypot.templatetags.honeypot as honeypot_filters
from django.template.loader import render_to_string from django.template.loader import render_to_string
from jinja2 import Environment, nodes from jinja2 import Environment, nodes
from jinja2.ext import Extension from jinja2.ext import Extension
from jinja2.parser import Parser
if TYPE_CHECKING:
from jinja2.parser import Parser
class HoneypotExtension(Extension): class HoneypotExtension(Extension):
+4 -1
View File
@@ -23,15 +23,18 @@
# #
import datetime import datetime
from typing import TYPE_CHECKING
import phonenumbers import phonenumbers
from aemark import markdown as md from aemark import markdown as md
from django import template from django import template
from django.forms import BoundField
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ngettext from django.utils.translation import ngettext
if TYPE_CHECKING:
from django.forms import BoundField
register = template.Library() register = template.Library()
+4 -1
View File
@@ -1,4 +1,5 @@
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
import freezegun import freezegun
import pytest import pytest
@@ -6,7 +7,6 @@ from aemark import markdown
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from model_bakery import baker from model_bakery import baker
@@ -16,6 +16,9 @@ from club.models import Club, Membership
from core.baker_recipes import board_user, subscriber_user from core.baker_recipes import board_user, subscriber_user
from core.models import AnonymousUser, Page, PageRev, User from core.models import AnonymousUser, Page, PageRev, User
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
class TestEditPage: class TestEditPage:
+5 -3
View File
@@ -17,16 +17,18 @@ from datetime import date, timedelta
# Image utils # Image utils
from io import BytesIO from io import BytesIO
from typing import Final from typing import TYPE_CHECKING, Final
import PIL import PIL
from django.conf import settings from django.conf import settings
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.files.uploadedfile import UploadedFile
from django.http import HttpRequest
from django.utils.timezone import localdate from django.utils.timezone import localdate
from PIL.Image import Image, Resampling from PIL.Image import Image, Resampling
if TYPE_CHECKING:
from django.core.files.uploadedfile import UploadedFile
from django.http import HttpRequest
RED_PIXEL_PNG: Final[bytes] = ( RED_PIXEL_PNG: Final[bytes] = (
b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53" b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53"
+4 -1
View File
@@ -13,7 +13,7 @@
# #
# #
import mimetypes import mimetypes
from pathlib import Path from typing import TYPE_CHECKING
from urllib.parse import quote, urljoin from urllib.parse import quote, urljoin
# This file contains all the views that concern the page model # This file contains all the views that concern the page model
@@ -48,6 +48,9 @@ from core.views.widgets.ajax_select import (
) )
from counter.utils import is_logged_in_counter from counter.utils import is_logged_in_counter
if TYPE_CHECKING:
from pathlib import Path
def send_raw_file(path: Path) -> HttpResponse: def send_raw_file(path: Path) -> HttpResponse:
"""Send a file located in the MEDIA_ROOT """Send a file located in the MEDIA_ROOT
+4 -1
View File
@@ -22,8 +22,8 @@
# #
import re import re
from copy import copy from copy import copy
from datetime import date, datetime
from io import BytesIO from io import BytesIO
from typing import TYPE_CHECKING
from captcha.fields import CaptchaField from captcha.fields import CaptchaField
from django import forms from django import forms
@@ -59,6 +59,9 @@ from core.views.widgets.ajax_select import (
) )
from core.views.widgets.markdown import MarkdownInput from core.views.widgets.markdown import MarkdownInput
if TYPE_CHECKING:
from datetime import date, datetime
# Widgets # Widgets
+6 -2
View File
@@ -22,11 +22,11 @@
# #
# #
from typing import TYPE_CHECKING
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import F from django.db.models import F
from django.db.models.query import QuerySet
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.views.generic import ListView, TemplateView from django.views.generic import ListView, TemplateView
@@ -34,6 +34,10 @@ from club.models import Club
from core.models import Notification, User from core.models import Notification, User
from core.schemas import UserFilterSchema from core.schemas import UserFilterSchema
if TYPE_CHECKING:
from django.db.models.query import QuerySet
from django.http import HttpRequest
class NotificationList(LoginRequiredMixin, ListView): class NotificationList(LoginRequiredMixin, ListView):
model = Notification model = Notification
+5 -3
View File
@@ -1,14 +1,16 @@
import copy import copy
import inspect import inspect
from typing import Any, ClassVar, LiteralString, Protocol, Unpack from typing import TYPE_CHECKING, Any, ClassVar, LiteralString, Protocol, Unpack
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest, HttpResponse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.safestring import SafeString
from django.views import View from django.views import View
from django.views.generic.base import ContextMixin, TemplateResponseMixin from django.views.generic.base import ContextMixin, TemplateResponseMixin
if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
from django.utils.safestring import SafeString
class TabedViewMixin(View): class TabedViewMixin(View):
"""Basic functions for displaying tabs in the template.""" """Basic functions for displaying tabs in the template."""
+4 -1
View File
@@ -27,6 +27,7 @@ from datetime import timedelta
# This file contains all the views that concern the user model # This file contains all the views that concern the user model
from operator import itemgetter from operator import itemgetter
from smtplib import SMTPException from smtplib import SMTPException
from typing import TYPE_CHECKING
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import login, views from django.contrib.auth import login, views
@@ -43,7 +44,6 @@ from django.shortcuts import get_object_or_404, redirect
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.safestring import SafeString
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from django.views.generic import ( from django.views.generic import (
@@ -73,6 +73,9 @@ from counter.models import Refilling, Selling
from eboutic.models import Invoice from eboutic.models import Invoice
from trombi.views import UserTrombiForm from trombi.views import UserTrombiForm
if TYPE_CHECKING:
from django.utils.safestring import SafeString
class SithLoginView(views.LoginView): class SithLoginView(views.LoginView):
"""The login View.""" """The login View."""
+7 -4
View File
@@ -1,15 +1,18 @@
from collections.abc import Collection from typing import TYPE_CHECKING, Any
from typing import Any
from django.contrib.staticfiles.storage import staticfiles_storage from django.contrib.staticfiles.storage import staticfiles_storage
from django.db.models import Model, QuerySet
from django.forms import Select, SelectMultiple from django.forms import Select, SelectMultiple
from ninja import ModelSchema
from pydantic import TypeAdapter from pydantic import TypeAdapter
from core.models import Group, SithFile, User from core.models import Group, SithFile, User
from core.schemas import GroupSchema, SithFileSchema, UserProfileSchema from core.schemas import GroupSchema, SithFileSchema, UserProfileSchema
if TYPE_CHECKING:
from collections.abc import Collection
from django.db.models import Model, QuerySet
from ninja import ModelSchema
class AutoCompleteSelectMixin: class AutoCompleteSelectMixin:
component_name = "autocomplete-select" component_name = "autocomplete-select"
+4 -2
View File
@@ -3,7 +3,7 @@ import math
import uuid import uuid
from collections import defaultdict from collections import defaultdict
from datetime import date, datetime, timezone from datetime import date, datetime, timezone
from typing import ClassVar from typing import TYPE_CHECKING, ClassVar
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django import forms from django import forms
@@ -11,7 +11,6 @@ from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import Exists, OuterRef, Q from django.db.models import Exists, OuterRef, Q
from django.forms import BaseModelFormSet from django.forms import BaseModelFormSet
from django.http import HttpRequest
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -59,6 +58,9 @@ from counter.widgets.ajax_select import (
AutoCompleteSelectProduct, AutoCompleteSelectProduct,
) )
if TYPE_CHECKING:
from django.http import HttpRequest
class BillingInfoForm(forms.ModelForm): class BillingInfoForm(forms.ModelForm):
class Meta: class Meta:
+4 -1
View File
@@ -1,5 +1,5 @@
from collections.abc import Iterable
from operator import attrgetter from operator import attrgetter
from typing import TYPE_CHECKING
from django.conf import settings from django.conf import settings
from django.core.mail import send_mass_mail from django.core.mail import send_mass_mail
@@ -13,6 +13,9 @@ from django.utils.translation import gettext as _
from core.models import User, UserQuerySet from core.models import User, UserQuerySet
from counter.models import AccountDump, Counter, Customer, Product, Selling from counter.models import AccountDump, Counter, Customer, Product, Selling
if TYPE_CHECKING:
from collections.abc import Iterable
class Command(BaseCommand): class Command(BaseCommand):
"""Effectively dump the inactive users. """Effectively dump the inactive users.
+3 -2
View File
@@ -1,13 +1,14 @@
from typing import TYPE_CHECKING, Callable from typing import TYPE_CHECKING, Callable
from django.http import HttpRequest, HttpResponse
from django.utils.functional import SimpleLazyObject from django.utils.functional import SimpleLazyObject
from core.models import User
from counter.models import Permanency from counter.models import Permanency
if TYPE_CHECKING: if TYPE_CHECKING:
from django.contrib.sessions.backends.base import SessionBase from django.contrib.sessions.backends.base import SessionBase
from django.http import HttpRequest, HttpResponse
from core.models import User
SESSION_PERMANENCES_KEY = "permanence_ids" SESSION_PERMANENCES_KEY = "permanence_ids"
@@ -1,11 +1,14 @@
# Generated by Django 4.2.17 on 2024-12-08 13:30 # Generated by Django 4.2.17 on 2024-12-08 13:30
from operator import attrgetter from operator import attrgetter
from typing import TYPE_CHECKING
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Count from django.db.models import Count
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def delete_duplicates(apps: StateApps, schema_editor): def delete_duplicates(apps: StateApps, schema_editor):
"""Delete cards of users with more than one student cards. """Delete cards of users with more than one student cards.
@@ -1,7 +1,11 @@
# Generated by Django 4.2.17 on 2024-12-15 17:53 # Generated by Django 4.2.17 on 2024-12-15 17:53
from typing import TYPE_CHECKING
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def move_priority_to_order(apps: StateApps, schema_editor): def move_priority_to_order(apps: StateApps, schema_editor):
@@ -1,9 +1,13 @@
# Generated by Django 5.2.8 on 2025-11-19 17:59 # Generated by Django 5.2.8 on 2025-11-19 17:59
from typing import TYPE_CHECKING
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Case, When from django.db.models import Case, When
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def migrate_selling_payment_method(apps: StateApps, schema_editor): def migrate_selling_payment_method(apps: StateApps, schema_editor):
# 0 <=> SITH_ACCOUNT is the default value, so no need to migrate it # 0 <=> SITH_ACCOUNT is the default value, so no need to migrate it
@@ -1,13 +1,16 @@
# Generated by Django 5.2.8 on 2026-02-10 15:40 # Generated by Django 5.2.8 on 2026-02-10 15:40
from operator import attrgetter from operator import attrgetter
from typing import TYPE_CHECKING
import django.utils.timezone import django.utils.timezone
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import OuterRef, Subquery from django.db.models import OuterRef, Subquery
from counter.models import Selling from counter.models import Selling
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def apply_product_history_dates(apps: StateApps, schema_editor): def apply_product_history_dates(apps: StateApps, schema_editor):
"""Approximate a posteriori the value of created_at and updated_at.""" """Approximate a posteriori the value of created_at and updated_at."""
+5 -1
View File
@@ -1,11 +1,15 @@
# Generated by Django 5.2.11 on 2026-02-18 13:30 # Generated by Django 5.2.11 on 2026-02-18 13:30
from typing import TYPE_CHECKING
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
import counter.fields import counter.fields
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def migrate_prices(apps: StateApps, schema_editor): def migrate_prices(apps: StateApps, schema_editor):
Product = apps.get_model("counter", "Product") Product = apps.get_model("counter", "Product")
+4 -1
View File
@@ -1,5 +1,5 @@
from collections.abc import Iterable
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
import freezegun import freezegun
import pytest import pytest
@@ -17,6 +17,9 @@ from counter.management.commands.dump_warning_mail import Command as WarningComm
from counter.models import AccountDump, Customer, Refilling, Selling from counter.models import AccountDump, Customer, Refilling, Selling
from subscription.models import Subscription from subscription.models import Subscription
if TYPE_CHECKING:
from collections.abc import Iterable
class TestAccountDump(TestCase): class TestAccountDump(TestCase):
@classmethod @classmethod
+4 -1
View File
@@ -1,9 +1,9 @@
import json import json
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from django_celery_beat.models import ClockedSchedule from django_celery_beat.models import ClockedSchedule
@@ -18,6 +18,9 @@ from counter.forms import (
) )
from counter.models import Product, ProductType, ScheduledProductAction from counter.models import Product, ProductType, ScheduledProductAction
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_edit_product(client: Client): def test_edit_product(client: Client):
+4 -1
View File
@@ -15,6 +15,7 @@
from dataclasses import asdict, dataclass from dataclasses import asdict, dataclass
from datetime import timedelta from datetime import timedelta
from decimal import Decimal from decimal import Decimal
from typing import TYPE_CHECKING
import pytest import pytest
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@@ -22,7 +23,6 @@ from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission, make_password from django.contrib.auth.models import Permission, make_password
from django.contrib.messages import DEFAULT_LEVELS, get_messages from django.contrib.messages import DEFAULT_LEVELS, get_messages
from django.http import HttpResponse
from django.shortcuts import resolve_url from django.shortcuts import resolve_url
from django.test import Client, TestCase from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
@@ -48,6 +48,9 @@ from counter.models import (
Selling, Selling,
) )
if TYPE_CHECKING:
from django.http import HttpResponse
def set_age(user: User, age: int): def set_age(user: User, age: int):
user.date_of_birth = localdate().replace(year=localdate().year - age) user.date_of_birth = localdate().replace(year=localdate().year - age)
+4 -1
View File
@@ -1,10 +1,10 @@
from datetime import date, datetime from datetime import date, datetime
from typing import TYPE_CHECKING
import pytest import pytest
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from model_bakery import baker from model_bakery import baker
@@ -16,6 +16,9 @@ from counter.baker_recipes import sale_recipe
from counter.forms import InvoiceCallForm from counter.forms import InvoiceCallForm
from counter.models import Customer, InvoiceCall, Selling from counter.models import Customer, InvoiceCall, Selling
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.parametrize( @pytest.mark.parametrize(
+5 -1
View File
@@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker, seq from model_bakery import baker, seq
@@ -8,6 +9,9 @@ from core.baker_recipes import board_user, subscriber_user
from core.models import Group, User from core.models import Group, User
from counter.models import ProductType from counter.models import ProductType
if TYPE_CHECKING:
from django.test import Client
@pytest.fixture @pytest.fixture
def product_types(db) -> list[ProductType]: def product_types(db) -> list[ProductType]:
+4 -1
View File
@@ -1,8 +1,11 @@
from typing import TYPE_CHECKING
from urllib.parse import urlparse from urllib.parse import urlparse
from django.http import HttpRequest
from django.urls import resolve from django.urls import resolve
if TYPE_CHECKING:
from django.http import HttpRequest
def is_logged_in_counter(request: HttpRequest) -> bool: def is_logged_in_counter(request: HttpRequest) -> bool:
"""Check if the request is sent from a device logged to a counter. """Check if the request is sent from a device logged to a counter.
+5 -1
View File
@@ -13,6 +13,8 @@
# #
# #
from typing import TYPE_CHECKING
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -22,7 +24,6 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from django.views.generic.edit import BaseFormView, UpdateView from django.views.generic.edit import BaseFormView, UpdateView
from core.models import User
from counter.forms import CashSummaryFormBase from counter.forms import CashSummaryFormBase
from counter.models import ( from counter.models import (
CashRegisterSummary, CashRegisterSummary,
@@ -35,6 +36,9 @@ from counter.views.mixins import (
CounterTabsMixin, CounterTabsMixin,
) )
if TYPE_CHECKING:
from core.models import User
class CashRegisterSummaryForm(forms.Form): class CashRegisterSummaryForm(forms.Form):
"""Provide the cash summary form.""" """Provide the cash summary form."""
+7 -3
View File
@@ -14,6 +14,7 @@
# #
import random import random
from collections import defaultdict from collections import defaultdict
from typing import TYPE_CHECKING
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@@ -22,14 +23,11 @@ from django.db.models import Q
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, resolve_url from django.shortcuts import get_object_or_404, redirect, resolve_url
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import SafeString
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import CreateView, FormView from django.views.generic import CreateView, FormView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from ninja.main import HttpRequest
from core.auth.mixins import CanViewMixin from core.auth.mixins import CanViewMixin
from core.models import User
from core.views.mixins import FragmentMixin, UseFragmentsMixin from core.views.mixins import FragmentMixin, UseFragmentsMixin
from counter.forms import BasketForm, RefillForm from counter.forms import BasketForm, RefillForm
from counter.models import ( from counter.models import (
@@ -44,6 +42,12 @@ from counter.utils import is_logged_in_counter
from counter.views.mixins import CounterTabsMixin from counter.views.mixins import CounterTabsMixin
from counter.views.student_card import StudentCardFormFragment from counter.views.student_card import StudentCardFormFragment
if TYPE_CHECKING:
from django.utils.safestring import SafeString
from ninja.main import HttpRequest
from core.models import User
def get_operator(request: HttpRequest, counter: Counter, customer: Customer) -> User: def get_operator(request: HttpRequest, counter: Counter, customer: Customer) -> User:
if counter.type != "BAR": if counter.type != "BAR":
+4 -1
View File
@@ -13,6 +13,7 @@
# #
# #
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@@ -21,7 +22,6 @@ from django.http import HttpRequest, HttpResponseRedirect
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.safestring import SafeString
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from django.views.generic import DetailView from django.views.generic import DetailView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
@@ -35,6 +35,9 @@ from counter.models import Counter, Permanency
from counter.utils import is_logged_in_counter from counter.utils import is_logged_in_counter
from counter.views.mixins import CounterTabsMixin from counter.views.mixins import CounterTabsMixin
if TYPE_CHECKING:
from django.utils.safestring import SafeString
class CounterLoginFragment(FragmentMixin, SingleObjectMixin, FormView): class CounterLoginFragment(FragmentMixin, SingleObjectMixin, FormView):
model = Counter model = Counter
+5 -1
View File
@@ -13,11 +13,12 @@
# #
# #
from typing import TYPE_CHECKING
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpRequest, HttpResponse from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import SafeString
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic.edit import DeleteView, FormView from django.views.generic.edit import DeleteView, FormView
@@ -27,6 +28,9 @@ from counter.forms import StudentCardForm
from counter.models import Customer, StudentCard from counter.models import Customer, StudentCard
from counter.utils import is_logged_in_counter from counter.utils import is_logged_in_counter
if TYPE_CHECKING:
from django.utils.safestring import SafeString
class StudentCardDeleteView(DeleteView): class StudentCardDeleteView(DeleteView):
"""View used to delete a card from a user. This is a fragment view !""" """View used to delete a card from a user. This is a fragment view !"""
+5 -2
View File
@@ -1,13 +1,12 @@
import re import re
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import TYPE_CHECKING
import freezegun import freezegun
import pytest import pytest
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.conf import settings from django.conf import settings
from django.http import HttpResponse
from django.test import TestCase from django.test import TestCase
from django.test.client import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import localdate, now from django.utils.timezone import localdate, now
from model_bakery import baker from model_bakery import baker
@@ -31,6 +30,10 @@ from counter.models import (
from counter.tests.test_counter import BasketItem from counter.tests.test_counter import BasketItem
from eboutic.models import Basket from eboutic.models import Basket
if TYPE_CHECKING:
from django.http import HttpResponse
from django.test.client import Client
@pytest.mark.django_db @pytest.mark.django_db
def test_get_eboutic(): def test_get_eboutic():
+4 -2
View File
@@ -1,7 +1,6 @@
from typing import Callable from typing import TYPE_CHECKING, Callable
import pytest import pytest
from django.test import Client
from django.urls import reverse from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from pytest_django.asserts import assertRedirects from pytest_django.asserts import assertRedirects
@@ -10,6 +9,9 @@ from core.baker_recipes import subscriber_user
from core.models import User from core.models import User
from counter.models import BillingInfo from counter.models import BillingInfo
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
class TestBillingInfo: class TestBillingInfo:
+4 -1
View File
@@ -1,6 +1,7 @@
from datetime import timedelta from datetime import timedelta
from itertools import groupby, islice from itertools import groupby, islice
from operator import attrgetter from operator import attrgetter
from typing import TYPE_CHECKING
from django import forms from django import forms
from django.conf import settings from django.conf import settings
@@ -13,7 +14,6 @@ from django.utils.translation import gettext_lazy as _
from club.forms import ClubRoleChoiceField from club.forms import ClubRoleChoiceField
from club.models import ClubRole, Membership from club.models import ClubRole, Membership
from club.widgets.ajax_select import AutoCompleteSelectMultipleClub from club.widgets.ajax_select import AutoCompleteSelectMultipleClub
from core.models import User
from core.views.forms import SelectDateTime from core.views.forms import SelectDateTime
from core.views.widgets.ajax_select import ( from core.views.widgets.ajax_select import (
AutoCompleteSelect, AutoCompleteSelect,
@@ -23,6 +23,9 @@ from core.views.widgets.ajax_select import (
from core.views.widgets.markdown import MarkdownInput from core.views.widgets.markdown import MarkdownInput
from election.models import Candidature, Election, ElectionList, Role from election.models import Candidature, Election, ElectionList, Role
if TYPE_CHECKING:
from core.models import User
class LimitedCheckboxField(forms.ModelMultipleChoiceField): class LimitedCheckboxField(forms.ModelMultipleChoiceField):
"""A `ModelMultipleChoiceField`, with a max limit of selectable inputs.""" """A `ModelMultipleChoiceField`, with a max limit of selectable inputs."""
+5 -1
View File
@@ -13,15 +13,19 @@
# #
# #
from typing import TYPE_CHECKING
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.test import Client
from django.urls import reverse from django.urls import reverse
from pytest_django.asserts import assertRedirects from pytest_django.asserts import assertRedirects
from core.models import User from core.models import User
from forum.models import Forum, ForumMessage, ForumTopic from forum.models import Forum, ForumMessage, ForumTopic
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
class TestTopicCreation: class TestTopicCreation:
-1
View File
@@ -91,7 +91,6 @@ class UE(models.Model):
# All texts about the UE # All texts about the UE
title = models.CharField(_("title"), max_length=300) title = models.CharField(_("title"), max_length=300)
manager = models.CharField(_("ue manager"), max_length=300)
objectives = models.TextField(_("objectives")) objectives = models.TextField(_("objectives"))
program = models.TextField(_("program")) program = models.TextField(_("program"))
skills = models.TextField(_("skills")) skills = models.TextField(_("skills"))
+21 -14
View File
@@ -17,25 +17,25 @@ authors = [
{ name = "Vial", email = "robin.trioux@utbm.fr" }, { name = "Vial", email = "robin.trioux@utbm.fr" },
] ]
license = { text = "GPL-3.0-only" } license = { text = "GPL-3.0-only" }
requires-python = "<4.0,>=3.12" requires-python = "<4.0,>=3.14"
dependencies = [ dependencies = [
"django>=5.2.17,<6.0.0", "django>=5.2.17,<6.0.0",
"django-ninja>=1.6.3,<2.0.0", "django-ninja>=1.7.0,<2.0.0",
"django-ninja-extra>=0.31.7", "django-ninja-extra>=0.31.7",
"Pillow>=12.3.0,<13.0.0", "Pillow>=12.3.0,<13.0.0",
"aemark>=0.1.1", "aemark>=0.1.1",
"django-jinja<3.0.0,>=2.11.0", "django-jinja<3.0.0,>=2.11.0",
"cryptography>=50.0.0,<51.0.0", "cryptography>=50.0.1,<51.0.0",
"django-phonenumber-field>=8.5.0,<9.0.0", "django-phonenumber-field>=8.5.0,<9.0.0",
"phonenumbers>=9.0.37,<10.0.0", "phonenumbers>=9.0.38,<10.0.0",
"reportlab>=5.0.1,<6.0.0", "reportlab>=5.0.1,<6.0.0",
"django-haystack>=3.4.0,<4.0.0", "django-haystack>=3.4.0,<4.0.0",
"xapian-haystack>=4.0.0,<5.0.0", "xapian-haystack>=4.0.0,<5.0.0",
"libsass>=0.23.0,<1.0.0", "libsass>=0.23.0,<1.0.0",
"django-ordered-model>=3.7.4,<4.0.0", "django-ordered-model>=3.7.4,<4.0.0",
"django-simple-captcha>=0.7.0,<1.0.0", "django-simple-captcha>=0.7.0,<1.0.0",
"python-dateutil>=2.9.0.post0,<3.0.0.0", "python-dateutil>=2.9.0.post0,<3.0.0",
"sentry-sdk>=2.68.0,<3.0.0", "sentry-sdk>=2.68.1,<3.0.0",
"jinja2>=3.1.6,<4.0.0", "jinja2>=3.1.6,<4.0.0",
"django-countries>=9.0.0,<10.0.0", "django-countries>=9.0.0,<10.0.0",
"dict2xml>=1.7.8,<2.0.0", "dict2xml>=1.7.8,<2.0.0",
@@ -43,13 +43,13 @@ dependencies = [
"tomli>=2.4.1,<3.0.0", "tomli>=2.4.1,<3.0.0",
"django-honeypot>=1.3.0,<2", "django-honeypot>=1.3.0,<2",
"pydantic-extra-types>=2.11.1,<3.0.0", "pydantic-extra-types>=2.11.1,<3.0.0",
"ical>=12.0.0", "ical>=14.1.1",
"redis[hiredis]>=6.4.0,<9.0.0", "redis[hiredis]>=6.4.0,<9.0.0",
"environs[django]>=15.1.0,<16", "environs[django]>=15.1.0,<16",
"requests>=2.34.2,<3.0.0", "requests>=2.34.2,<3.0.0",
"honcho>=2.0.0", "honcho>=2.0.0",
"psutil>=7.2.2,<8.0.0", "psutil>=7.2.2,<8.0.0",
"celery[redis]>=5.6.3,<8", "celery[redis]>=5.6.2,<7",
"django-celery-results>=2.6.0", "django-celery-results>=2.6.0",
"django-celery-beat>=2.9.0", "django-celery-beat>=2.9.0",
] ]
@@ -63,12 +63,12 @@ prod = [
"psycopg[c]>=3.3.4,<4.0.0", "psycopg[c]>=3.3.4,<4.0.0",
] ]
dev = [ dev = [
"django-debug-toolbar>=7.1.1,<8", "django-debug-toolbar>=8.0.0,<9",
"ipython>=9.16.1,<10.0.0", "ipython>=9.17.1,<10.0.0",
"pre-commit>=4.6.0,<5.0.0", "pre-commit>=4.6.1,<5.0.0",
"ruff>=0.16.0,<1.0.0", "ruff>=0.16.6,<1.0.0",
"djhtml>=3.0.11,<4.0.0", "djhtml>=3.0.11,<4.0.0",
"faker>=40.37.0,<41.0.0", "faker>=40.38.0,<41.0.0",
"rjsmin>=1.2.5,<2.0.0", "rjsmin>=1.2.5,<2.0.0",
] ]
tests = [ tests = [
@@ -78,7 +78,7 @@ tests = [
"pytest-django>=4.14.0,<5.0.0", "pytest-django>=4.14.0,<5.0.0",
"model-bakery>=1.24.0,<2.0.0", "model-bakery>=1.24.0,<2.0.0",
"beautifulsoup4>=4.15.0,<5", "beautifulsoup4>=4.15.0,<5",
"lxml>=6.1.2,<7", "lxml>=6.1.3,<7",
] ]
docs = [ docs = [
"mkdocs>=1.6.1,<2.0.0", "mkdocs>=1.6.1,<2.0.0",
@@ -132,6 +132,13 @@ ignore = [
"RUF012" # mutable class attributes. This rule doesn't integrate well with django "RUF012" # mutable class attributes. This rule doesn't integrate well with django
] ]
[tool.ruff.lint.per-file-ignores]
# schemas and API routes are built upon ninja and pydantic,
# which heavily rely on type annotations at runtime.
# Systematically using TYPE_CHECKING blocks in those files is NOT safe
"schemas.py" = ["TCH"]
"api.py" = ["TCH"]
[tool.ruff.lint.pydocstyle] [tool.ruff.lint.pydocstyle]
convention = "google" convention = "google"
+4 -1
View File
@@ -1,8 +1,8 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING
import pytest import pytest
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import localtime from django.utils.timezone import localtime
from model_bakery import baker from model_bakery import baker
@@ -10,6 +10,9 @@ from pytest_django.asserts import assertRedirects
from core.models import BanGroup, User, UserBan from core.models import BanGroup, User, UserBan
if TYPE_CHECKING:
from django.test import Client
@pytest.fixture @pytest.fixture
def operator(db) -> User: def operator(db) -> User:
+2 -1
View File
@@ -9,7 +9,6 @@ from django.utils.timezone import get_current_timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from PIL import Image from PIL import Image
from core.models import User
from core.utils import resize_image from core.utils import resize_image
from core.views import MultipleImageField from core.views import MultipleImageField
from core.views.forms import SelectDate from core.views.forms import SelectDate
@@ -20,6 +19,8 @@ from sas.widgets.ajax_select import AutoCompleteSelectAlbum
if TYPE_CHECKING: if TYPE_CHECKING:
from django.db.models.fields.files import FieldFile from django.db.models.fields.files import FieldFile
from core.models import User
class AlbumCreateForm(forms.ModelForm): class AlbumCreateForm(forms.ModelForm):
class Meta: class Meta:
+4 -2
View File
@@ -1,14 +1,13 @@
import random import random
import string import string
from pathlib import Path from pathlib import Path
from typing import Callable from typing import TYPE_CHECKING, Callable
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.timezone import localdate from django.utils.timezone import localdate
@@ -23,6 +22,9 @@ from sas.baker_recipes import picture_recipe
from sas.forms import AlbumEditForm from sas.forms import AlbumEditForm
from sas.models import Album from sas.models import Album
if TYPE_CHECKING:
from django.test import Client
@pytest.fixture @pytest.fixture
def sas_root(db) -> Album: def sas_root(db) -> Album:
+4 -2
View File
@@ -12,7 +12,7 @@
# OR WITHIN THE LOCAL FILE "LICENSE" # OR WITHIN THE LOCAL FILE "LICENSE"
# #
# #
from typing import Any from typing import TYPE_CHECKING, Any
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -21,7 +21,6 @@ from django.db.models import Count, OuterRef, Subquery
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import SafeString
from django.views.generic import CreateView, DetailView, TemplateView from django.views.generic import CreateView, DetailView, TemplateView
from django.views.generic.edit import FormView, UpdateView from django.views.generic.edit import FormView, UpdateView
@@ -40,6 +39,9 @@ from sas.forms import (
) )
from sas.models import Album, PeoplePictureRelation, Picture from sas.models import Album, PeoplePictureRelation, Picture
if TYPE_CHECKING:
from django.utils.safestring import SafeString
class AlbumCreateFragment(FragmentMixin, CreateView): class AlbumCreateFragment(FragmentMixin, CreateView):
model = Album model = Album
+4 -1
View File
@@ -2,12 +2,15 @@ import logging
import signal import signal
import subprocess import subprocess
import sys import sys
from pathlib import Path from typing import TYPE_CHECKING
import psutil import psutil
from sith import settings from sith import settings
if TYPE_CHECKING:
from pathlib import Path
def get_pid_file(procfile: Path) -> Path: def get_pid_file(procfile: Path) -> Path:
"""Get the PID file associated with a procfile""" """Get the PID file associated with a procfile"""
+5 -2
View File
@@ -1,11 +1,14 @@
from contextlib import nullcontext as does_not_raise from contextlib import nullcontext as does_not_raise
from typing import TYPE_CHECKING
import pytest import pytest
from _pytest.raises import RaisesExc
from django.test import Client
from django.test.utils import override_settings from django.test.utils import override_settings
from django.urls import reverse from django.urls import reverse
if TYPE_CHECKING:
from _pytest.raises import RaisesExc
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.parametrize( @pytest.mark.parametrize(
+4 -1
View File
@@ -1,14 +1,17 @@
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles.finders import find from django.contrib.staticfiles.finders import find
from django.contrib.staticfiles.storage import ( from django.contrib.staticfiles.storage import (
ManifestStaticFilesStorage, ManifestStaticFilesStorage,
) )
from django.core.files.storage import Storage
from staticfiles.processors import JS, JSBundler, Scss from staticfiles.processors import JS, JSBundler, Scss
if TYPE_CHECKING:
from django.core.files.storage import Storage
class ManifestPostProcessingStorage(ManifestStaticFilesStorage): class ManifestPostProcessingStorage(ManifestStaticFilesStorage):
def url(self, name: str, *, force: bool = False) -> str: def url(self, name: str, *, force: bool = False) -> str:
@@ -1,7 +1,11 @@
# Generated by Django 5.2.3 on 2025-09-08 05:38 # Generated by Django 5.2.3 on 2025-09-08 05:38
from typing import TYPE_CHECKING
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def rename_enums(apps: StateApps, schema_editor): def rename_enums(apps: StateApps, schema_editor):
@@ -1,12 +1,16 @@
# Generated by Django 5.2.17 on 2026-09-01 08:44 # Generated by Django 5.2.17 on 2026-09-01 08:44
from typing import TYPE_CHECKING
import django.utils.timezone import django.utils.timezone
from django.db import migrations, models from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import F, Value from django.db.models import F, Value
from django.db.models.functions import Least from django.db.models.functions import Least
from django.utils.timezone import now from django.utils.timezone import now
if TYPE_CHECKING:
from django.db.migrations.state import StateApps
def make_default_creation_date(apps: StateApps, schema_editor): def make_default_creation_date(apps: StateApps, schema_editor):
Subscription = apps.get_model("subscription", "Subscription") Subscription = apps.get_model("subscription", "Subscription")
+4 -2
View File
@@ -1,13 +1,12 @@
"""Tests focused on testing subscription creation""" """Tests focused on testing subscription creation"""
from datetime import date, timedelta from datetime import date, timedelta
from typing import Callable from typing import TYPE_CHECKING, Callable
import pytest import pytest
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.test import Client
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import localdate from django.utils.timezone import localdate
from model_bakery import baker from model_bakery import baker
@@ -19,6 +18,9 @@ from counter.models import Customer
from subscription.forms import SubscriptionExistingUserForm, SubscriptionNewUserForm from subscription.forms import SubscriptionExistingUserForm, SubscriptionNewUserForm
from subscription.models import Subscription from subscription.models import Subscription
if TYPE_CHECKING:
from django.test import Client
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.parametrize( @pytest.mark.parametrize(
Generated
+296 -642
View File
File diff suppressed because it is too large Load Diff