mirror of
https://github.com/ae-utbm/sith.git
synced 2026-02-08 16:41:29 +00:00
Compare commits
11 Commits
test_elect
...
cb-message
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df2e65a991 | ||
|
|
de776045a8 | ||
|
|
367ea703ce | ||
|
|
bdcb802da8 | ||
|
|
4e4b5a39f7 | ||
| 51534629ed | |||
|
c042c8e8a3
|
|||
|
|
5af894060a | ||
| 679b8dac1c | |||
|
e9eb3dc17d
|
|||
|
|
53a3dc0060 |
@@ -1,7 +1,7 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.14.4
|
rev: v0.15.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check # just check the code, and print the errors
|
- id: ruff-check # just check the code, and print the errors
|
||||||
- id: ruff-check # actually fix the fixable errors, but print nothing
|
- id: ruff-check # actually fix the fixable errors, but print nothing
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ class TestFileModerationView:
|
|||||||
(lambda: None, 403), # Anonymous user
|
(lambda: None, 403), # Anonymous user
|
||||||
(lambda: baker.make(User, is_superuser=True), 200),
|
(lambda: baker.make(User, is_superuser=True), 200),
|
||||||
(lambda: baker.make(User), 403),
|
(lambda: baker.make(User), 403),
|
||||||
(lambda: subscriber_user.make(), 403),
|
(subscriber_user.make, 403),
|
||||||
(lambda: old_subscriber_user.make(), 403),
|
(old_subscriber_user.make, 403),
|
||||||
(lambda: board_user.make(), 403),
|
(board_user.make, 403),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_view_access(
|
def test_view_access(
|
||||||
@@ -262,7 +262,7 @@ def test_apply_rights_recursively():
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("user_receipe", "file", "expected_status"),
|
("user_recipe", "file", "expected_status"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
lambda: None,
|
lambda: None,
|
||||||
@@ -279,21 +279,21 @@ def test_apply_rights_recursively():
|
|||||||
403,
|
403,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
lambda: subscriber_user.make(),
|
subscriber_user.make,
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
"test.jpg", content=RED_PIXEL_PNG, content_type="image/jpg"
|
"test.jpg", content=RED_PIXEL_PNG, content_type="image/jpg"
|
||||||
),
|
),
|
||||||
200,
|
200,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
old_subscriber_user.make,
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
"test.jpg", content=RED_PIXEL_PNG, content_type="image/jpg"
|
"test.jpg", content=RED_PIXEL_PNG, content_type="image/jpg"
|
||||||
),
|
),
|
||||||
200,
|
200,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
old_subscriber_user.make,
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
"ttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestesttesttesttesttesttesttesttesttesttesttesttest.jpg",
|
"ttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestesttesttesttesttesttesttesttesttesttesttesttest.jpg",
|
||||||
content=RED_PIXEL_PNG,
|
content=RED_PIXEL_PNG,
|
||||||
@@ -302,21 +302,21 @@ def test_apply_rights_recursively():
|
|||||||
200,
|
200,
|
||||||
), # very long file name
|
), # very long file name
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
old_subscriber_user.make,
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
"test.jpg", content=b"invalid", content_type="image/jpg"
|
"test.jpg", content=b"invalid", content_type="image/jpg"
|
||||||
),
|
),
|
||||||
422,
|
422,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
old_subscriber_user.make,
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
"test.jpg", content=RED_PIXEL_PNG, content_type="invalid"
|
"test.jpg", content=RED_PIXEL_PNG, content_type="invalid"
|
||||||
),
|
),
|
||||||
200, # PIL can guess
|
200, # PIL can guess
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
old_subscriber_user.make,
|
||||||
SimpleUploadedFile("test.jpg", content=b"invalid", content_type="invalid"),
|
SimpleUploadedFile("test.jpg", content=b"invalid", content_type="invalid"),
|
||||||
422,
|
422,
|
||||||
),
|
),
|
||||||
@@ -324,11 +324,11 @@ def test_apply_rights_recursively():
|
|||||||
)
|
)
|
||||||
def test_quick_upload_image(
|
def test_quick_upload_image(
|
||||||
client: Client,
|
client: Client,
|
||||||
user_receipe: Callable[[], User | None],
|
user_recipe: Callable[[], User | None],
|
||||||
file: UploadedFile | None,
|
file: UploadedFile | None,
|
||||||
expected_status: int,
|
expected_status: int,
|
||||||
):
|
):
|
||||||
if (user := user_receipe()) is not None:
|
if (user := user_recipe()) is not None:
|
||||||
client.force_login(user)
|
client.force_login(user)
|
||||||
resp = client.post(
|
resp = client.post(
|
||||||
reverse("api:quick_upload_image"), {"file": file} if file is not None else {}
|
reverse("api:quick_upload_image"), {"file": file} if file is not None else {}
|
||||||
|
|||||||
@@ -418,9 +418,7 @@ class TestUserQuerySetViewableBy:
|
|||||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||||
assert set(viewable) == {users[0], users[1]}
|
assert set(viewable) == {users[0], users[1]}
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize("user_factory", [lambda: baker.make(User), AnonymousUser])
|
||||||
"user_factory", [lambda: baker.make(User), lambda: AnonymousUser()]
|
|
||||||
)
|
|
||||||
def test_not_subscriber(self, users: list[User], user_factory):
|
def test_not_subscriber(self, users: list[User], user_factory):
|
||||||
user = user_factory()
|
user = user_factory()
|
||||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ class Command(BaseCommand):
|
|||||||
"""Fetch the users which have a pending account dump."""
|
"""Fetch the users which have a pending account dump."""
|
||||||
threshold = now() - settings.SITH_ACCOUNT_DUMP_DELTA
|
threshold = now() - settings.SITH_ACCOUNT_DUMP_DELTA
|
||||||
ongoing_dump_operations: QuerySet[AccountDump] = (
|
ongoing_dump_operations: QuerySet[AccountDump] = (
|
||||||
AccountDump.objects.ongoing()
|
AccountDump.objects.ongoing().filter(
|
||||||
.filter(customer__user=OuterRef("pk"), warning_mail_sent_at__lt=threshold)
|
customer__user=OuterRef("pk"), warning_mail_sent_at__lt=threshold
|
||||||
) # fmt: off
|
)
|
||||||
# cf. https://github.com/astral-sh/ruff/issues/14103
|
)
|
||||||
return (
|
return (
|
||||||
User.objects.filter(Exists(ongoing_dump_operations))
|
User.objects.filter(Exists(ongoing_dump_operations))
|
||||||
.annotate(
|
.annotate(
|
||||||
|
|||||||
@@ -447,8 +447,7 @@ class Product(models.Model):
|
|||||||
buying_groups = list(self.buying_groups.all())
|
buying_groups = list(self.buying_groups.all())
|
||||||
if not buying_groups:
|
if not buying_groups:
|
||||||
return True
|
return True
|
||||||
res = any(user.is_in_group(pk=group.id) for group in buying_groups)
|
return any(user.is_in_group(pk=group.id) for group in buying_groups)
|
||||||
return res
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profit(self):
|
def profit(self):
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li x-show="getBasketSize() === 0">{% trans %}This basket is empty{% endtrans %}</li>
|
<li x-show="getBasketSize() === 0">{% trans %}This basket is empty{% endtrans %}</li>
|
||||||
<template x-for="(item, index) in Object.values(basket)">
|
<template x-for="(item, index) in Object.values(basket)" :key="item.product.id">
|
||||||
<li>
|
<li>
|
||||||
<template x-for="error in item.errors">
|
<template x-for="error in item.errors">
|
||||||
<div class="alert alert-red" x-text="error">
|
<div class="alert alert-red" x-text="error">
|
||||||
|
|||||||
@@ -43,12 +43,13 @@ def get_eboutic_products(user: User) -> list[Product]:
|
|||||||
products = (
|
products = (
|
||||||
get_eboutic()
|
get_eboutic()
|
||||||
.products.filter(product_type__isnull=False)
|
.products.filter(product_type__isnull=False)
|
||||||
.filter(archived=False)
|
.filter(archived=False, limit_age__lte=user.age)
|
||||||
.filter(limit_age__lte=user.age)
|
.annotate(
|
||||||
.annotate(order=F("product_type__order"))
|
order=F("product_type__order"),
|
||||||
.annotate(category=F("product_type__name"))
|
category=F("product_type__name"),
|
||||||
.annotate(category_comment=F("product_type__comment"))
|
category_comment=F("product_type__comment"),
|
||||||
.annotate(price=F("selling_price")) # <-- selected price for basket validation
|
price=F("selling_price"), # <-- selected price for basket validation
|
||||||
|
)
|
||||||
.prefetch_related("buying_groups") # <-- used in `Product.can_be_sold_to`
|
.prefetch_related("buying_groups") # <-- used in `Product.can_be_sold_to`
|
||||||
)
|
)
|
||||||
return [p for p in products if p.can_be_sold_to(user)]
|
return [p for p in products if p.can_be_sold_to(user)]
|
||||||
|
|||||||
@@ -77,6 +77,14 @@
|
|||||||
value="{% trans %}Pay with credit card{% endtrans %}"
|
value="{% trans %}Pay with credit card{% endtrans %}"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-yellow">
|
||||||
|
{% trans trimmed %}
|
||||||
|
Credit card payments are currently disabled on the eboutic.
|
||||||
|
You may still refill your account in one of the AE counters.
|
||||||
|
Please excuse for the disagreement.
|
||||||
|
{% endtrans %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if basket.contains_refilling_item %}
|
{% if basket.contains_refilling_item %}
|
||||||
<p>{% trans %}AE account payment disabled because your basket contains refilling items.{% endtrans %}</p>
|
<p>{% trans %}AE account payment disabled because your basket contains refilling items.{% endtrans %}</p>
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ class CandidateForm(forms.ModelForm):
|
|||||||
class VoteForm(forms.Form):
|
class VoteForm(forms.Form):
|
||||||
def __init__(self, election: Election, user: User, *args, **kwargs):
|
def __init__(self, election: Election, user: User, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if not election.can_vote(user):
|
|
||||||
return
|
|
||||||
for role in election.roles.all():
|
for role in election.roles.all():
|
||||||
cand = role.candidatures
|
cand = role.candidatures
|
||||||
if role.max_choice > 1:
|
if role.max_choice > 1:
|
||||||
@@ -74,6 +72,7 @@ class VoteForm(forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=forms.RadioSelect(),
|
widget=forms.RadioSelect(),
|
||||||
empty_label=_("Blank vote"),
|
empty_label=_("Blank vote"),
|
||||||
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3 class="election__title">{{ election.title }}</h3>
|
<h3 class="election__title">{{ election.title }}</h3>
|
||||||
|
{% if not user.has_perm("core.view_user") %}
|
||||||
|
<div class="alert alert-red">
|
||||||
|
{% trans %}Candidate pictures won't display for privacy reasons.{% endtrans %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<p class="election__description">{{ election.description }}</p>
|
<p class="election__description">{{ election.description }}</p>
|
||||||
<hr>
|
<hr>
|
||||||
<section class="election_details">
|
<section class="election_details">
|
||||||
@@ -117,7 +122,7 @@
|
|||||||
{%- if role.max_choice == 1 and show_vote_buttons %}
|
{%- if role.max_choice == 1 and show_vote_buttons %}
|
||||||
<div class="radio-btn">
|
<div class="radio-btn">
|
||||||
{% set input_id = "blank_vote_" + role.id|string %}
|
{% set input_id = "blank_vote_" + role.id|string %}
|
||||||
<input id="{{ input_id }}" type="radio" name="{{ role.title }}">
|
<input id="{{ input_id }}" type="radio" name="{{ role.title }}" value="" checked>
|
||||||
<label for="{{ input_id }}">
|
<label for="{{ input_id }}">
|
||||||
<span>{% trans %}Choose blank vote{% endtrans %}</span>
|
<span>{% trans %}Choose blank vote{% endtrans %}</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -185,6 +190,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
{% if not user.is_anonymous %}
|
||||||
<section class="buttons">
|
<section class="buttons">
|
||||||
{%- if (election.can_candidate(user) and election.is_candidature_active) or (user.can_edit(election) and election.is_vote_editable) %}
|
{%- if (election.can_candidate(user) and election.is_candidature_active) or (user.can_edit(election) and election.is_vote_editable) %}
|
||||||
<a class="button" href="{{ url('election:candidate', election_id=object.id) }}">{% trans %}Candidate{% endtrans %}</a>
|
<a class="button" href="{{ url('election:candidate', election_id=object.id) }}">{% trans %}Candidate{% endtrans %}</a>
|
||||||
@@ -207,4 +213,5 @@
|
|||||||
<button class="button button_send" form="vote-form">{% trans %}Submit the vote !{% endtrans %}</button>
|
<button class="button button_send" form="vote-form">{% trans %}Submit the vote !{% endtrans %}</button>
|
||||||
</section>
|
</section>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
from cryptography.utils import cached_property
|
from cryptography.utils import cached_property
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import (
|
from django.contrib.auth.mixins import (
|
||||||
LoginRequiredMixin,
|
LoginRequiredMixin,
|
||||||
PermissionRequiredMixin,
|
PermissionRequiredMixin,
|
||||||
@@ -10,8 +11,9 @@ from django.contrib.auth.mixins import (
|
|||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
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 CreateView, DeleteView, FormView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ class ElectionListArchivedView(CanViewMixin, ListView):
|
|||||||
|
|
||||||
|
|
||||||
class ElectionDetailView(CanViewMixin, DetailView):
|
class ElectionDetailView(CanViewMixin, DetailView):
|
||||||
"""Details an election responsability by responsability."""
|
"""Details an election responsibility by responsibility."""
|
||||||
|
|
||||||
model = Election
|
model = Election
|
||||||
template_name = "election/election_detail.jinja"
|
template_name = "election/election_detail.jinja"
|
||||||
@@ -83,7 +85,7 @@ class ElectionDetailView(CanViewMixin, DetailView):
|
|||||||
return super().get(request, *arg, **kwargs)
|
return super().get(request, *arg, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Add additionnal data to the template."""
|
"""Add additional data to the template."""
|
||||||
user: User = self.request.user
|
user: User = self.request.user
|
||||||
return super().get_context_data(**kwargs) | {
|
return super().get_context_data(**kwargs) | {
|
||||||
"election_form": VoteForm(self.object, user),
|
"election_form": VoteForm(self.object, user),
|
||||||
@@ -101,7 +103,7 @@ class ElectionDetailView(CanViewMixin, DetailView):
|
|||||||
|
|
||||||
|
|
||||||
class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
||||||
"""Alows users to vote."""
|
"""Allows users to vote."""
|
||||||
|
|
||||||
form_class = VoteForm
|
form_class = VoteForm
|
||||||
template_name = "election/election_detail.jinja"
|
template_name = "election/election_detail.jinja"
|
||||||
@@ -111,6 +113,9 @@ class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
|||||||
return get_object_or_404(Election, pk=self.kwargs["election_id"])
|
return get_object_or_404(Election, pk=self.kwargs["election_id"])
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
|
if not self.election.can_vote(self.request.user):
|
||||||
|
return False
|
||||||
|
|
||||||
groups = set(self.election.vote_groups.values_list("id", flat=True))
|
groups = set(self.election.vote_groups.values_list("id", flat=True))
|
||||||
if (
|
if (
|
||||||
settings.SITH_GROUP_SUBSCRIBERS_ID in groups
|
settings.SITH_GROUP_SUBSCRIBERS_ID in groups
|
||||||
@@ -150,11 +155,17 @@ class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
|||||||
self.vote(data)
|
self.vote(data)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
messages.error(self.request, _("Form is invalid"))
|
||||||
|
return redirect(
|
||||||
|
reverse("election:detail", kwargs={"election_id": self.election.id}),
|
||||||
|
)
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
def get_success_url(self, **kwargs):
|
||||||
return reverse_lazy("election:detail", kwargs={"election_id": self.election.id})
|
return reverse_lazy("election:detail", kwargs={"election_id": self.election.id})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Add additionnal data to the template."""
|
"""Add additional data to the template."""
|
||||||
kwargs = super().get_context_data(**kwargs)
|
kwargs = super().get_context_data(**kwargs)
|
||||||
kwargs["object"] = self.election
|
kwargs["object"] = self.election
|
||||||
kwargs["election"] = self.election
|
kwargs["election"] = self.election
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-12-19 23:10+0100\n"
|
"POT-Creation-Date: 2026-02-08 16:14+0100\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@@ -3944,6 +3944,16 @@ msgstr "Solde restant : "
|
|||||||
msgid "Pay with credit card"
|
msgid "Pay with credit card"
|
||||||
msgstr "Payer avec une carte bancaire"
|
msgstr "Payer avec une carte bancaire"
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_checkout.jinja
|
||||||
|
msgid ""
|
||||||
|
"Credit card payments are currently disabled on the eboutic. You may still "
|
||||||
|
"refill your account in one of the AE counters. Please excuse for the "
|
||||||
|
"disagreement."
|
||||||
|
msgstr ""
|
||||||
|
"Les paiements par carte bancaire sont actuellement désactivés sur l'eboutic. "
|
||||||
|
"Vous pouvez cependant toujours recharger votre compte dans un des lieux de vie de l'AE. "
|
||||||
|
"Veuillez nous excuser pour le désagrément."
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_checkout.jinja
|
#: eboutic/templates/eboutic/eboutic_checkout.jinja
|
||||||
msgid ""
|
msgid ""
|
||||||
"AE account payment disabled because your basket contains refilling items."
|
"AE account payment disabled because your basket contains refilling items."
|
||||||
@@ -4108,6 +4118,12 @@ msgstr "Candidater"
|
|||||||
msgid "Candidature are closed for this election"
|
msgid "Candidature are closed for this election"
|
||||||
msgstr "Les candidatures sont fermées pour cette élection"
|
msgstr "Les candidatures sont fermées pour cette élection"
|
||||||
|
|
||||||
|
#: election/templates/election/election_detail.jinja
|
||||||
|
msgid "Candidate pictures won't display for privacy reasons."
|
||||||
|
msgstr ""
|
||||||
|
"La photo du candidat ne s'affiche pas pour "
|
||||||
|
"des raisons de respect de la vie privée."
|
||||||
|
|
||||||
#: election/templates/election/election_detail.jinja
|
#: election/templates/election/election_detail.jinja
|
||||||
msgid "Polls close "
|
msgid "Polls close "
|
||||||
msgstr "Votes fermés"
|
msgstr "Votes fermés"
|
||||||
@@ -4183,6 +4199,10 @@ msgstr "au"
|
|||||||
msgid "Polls open from"
|
msgid "Polls open from"
|
||||||
msgstr "Votes ouverts du"
|
msgstr "Votes ouverts du"
|
||||||
|
|
||||||
|
#: election/views.py
|
||||||
|
msgid "Form is invalid"
|
||||||
|
msgstr "Formulaire invalide"
|
||||||
|
|
||||||
#: forum/models.py
|
#: forum/models.py
|
||||||
msgid "is a category"
|
msgid "is a category"
|
||||||
msgstr "est une catégorie"
|
msgstr "est une catégorie"
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ dev = [
|
|||||||
"django-debug-toolbar>=6.1.0,<7",
|
"django-debug-toolbar>=6.1.0,<7",
|
||||||
"ipython>=9.7.0,<10.0.0",
|
"ipython>=9.7.0,<10.0.0",
|
||||||
"pre-commit>=4.3.0,<5.0.0",
|
"pre-commit>=4.3.0,<5.0.0",
|
||||||
"ruff>=0.14.4,<1.0.0",
|
"ruff>=0.15.0,<1.0.0",
|
||||||
"djhtml>=3.0.10,<4.0.0",
|
"djhtml>=3.0.10,<4.0.0",
|
||||||
"faker>=37.12.0,<38.0.0",
|
"faker>=37.12.0,<38.0.0",
|
||||||
"rjsmin>=1.2.5,<2.0.0",
|
"rjsmin>=1.2.5,<2.0.0",
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ document.addEventListener("alpine:init", () => {
|
|||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this.downloadPictures.map(async (p: PictureSchema) => {
|
this.downloadPictures.map(async (p: PictureSchema) => {
|
||||||
const imgName = `${p.album}/IMG_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`;
|
const imgName = `${p.album.name}/IMG_${p.id}_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`;
|
||||||
return zipWriter.add(imgName, new HttpReader(p.full_size_url), {
|
return zipWriter.add(imgName, new HttpReader(p.full_size_url), {
|
||||||
level: 9,
|
level: 9,
|
||||||
lastModDate: new Date(p.date),
|
lastModDate: new Date(p.date),
|
||||||
|
|||||||
41
uv.lock
generated
41
uv.lock
generated
@@ -1969,28 +1969,27 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.6"
|
version = "0.15.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" },
|
{ url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" },
|
{ url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" },
|
{ url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" },
|
{ url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" },
|
{ url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" },
|
{ url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" },
|
{ url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" },
|
{ url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" },
|
{ url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" },
|
{ url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" },
|
{ url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" },
|
{ url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" },
|
{ url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" },
|
{ url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" },
|
{ url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" },
|
{ url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2121,7 +2120,7 @@ dev = [
|
|||||||
{ name = "ipython", specifier = ">=9.7.0,<10.0.0" },
|
{ name = "ipython", specifier = ">=9.7.0,<10.0.0" },
|
||||||
{ name = "pre-commit", specifier = ">=4.3.0,<5.0.0" },
|
{ name = "pre-commit", specifier = ">=4.3.0,<5.0.0" },
|
||||||
{ name = "rjsmin", specifier = ">=1.2.5,<2.0.0" },
|
{ name = "rjsmin", specifier = ">=1.2.5,<2.0.0" },
|
||||||
{ name = "ruff", specifier = ">=0.14.4,<1.0.0" },
|
{ name = "ruff", specifier = ">=0.15.0,<1.0.0" },
|
||||||
]
|
]
|
||||||
docs = [
|
docs = [
|
||||||
{ name = "mkdocs", specifier = ">=1.6.1,<2.0.0" },
|
{ name = "mkdocs", specifier = ">=1.6.1,<2.0.0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user