Merge pull request #1044 from ae-utbm/fix-deprecations

Fix some deprecations
This commit is contained in:
thomas girod 2025-03-14 12:37:11 +01:00 committed by GitHub
commit bb3dfb7e8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 177 additions and 71 deletions

View File

@ -32,7 +32,7 @@ class TestRefoundAccount(TestCase):
@classmethod
def setUpTestData(cls):
cls.skia = User.objects.get(username="skia")
# reffil skia's account
# refill skia's account
cls.skia.customer.amount = 800
cls.skia.customer.save()
cls.refound_account_url = reverse("accounting:refound_account")

View File

@ -17,7 +17,7 @@ import collections
from django import forms
from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import transaction
from django.db.models import Sum
@ -846,27 +846,16 @@ class CloseCustomerAccountForm(forms.Form):
)
class RefoundAccountView(FormView):
class RefoundAccountView(UserPassesTestMixin, FormView):
"""Create a selling with the same amount than the current user money."""
template_name = "accounting/refound_account.jinja"
form_class = CloseCustomerAccountForm
def permission(self, user):
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
else:
raise PermissionDenied
def dispatch(self, request, *arg, **kwargs):
res = super().dispatch(request, *arg, **kwargs)
if self.permission(request.user):
return res
def post(self, request, *arg, **kwargs):
self.operator = request.user
if self.permission(request.user):
return super().post(self, request, *arg, **kwargs)
def test_func(self):
return self.request.user.is_root or self.request.user.is_in_group(
pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID
)
def form_valid(self, form):
self.customer = form.cleaned_data["user"]
@ -887,7 +876,7 @@ class RefoundAccountView(FormView):
label=_("Refound account"),
unit_price=uprice,
quantity=1,
seller=self.operator,
seller=self.request.user,
customer=self.customer.customer,
club=refound_club,
counter=refound_club_counter,

View File

@ -2,7 +2,7 @@ from datetime import datetime
from ninja import FilterSchema, ModelSchema
from ninja_extra import service_resolver
from ninja_extra.controllers import RouteContext
from ninja_extra.context import RouteContext
from pydantic import Field
from club.schemas import ClubProfileSchema

View File

@ -169,10 +169,9 @@ class CanCreateMixin(View):
super().__init__(*args, **kwargs)
def dispatch(self, request, *arg, **kwargs):
res = super().dispatch(request, *arg, **kwargs)
if not request.user.is_authenticated:
raise PermissionDenied
return res
return super().dispatch(request, *arg, **kwargs)
def form_valid(self, form):
obj = form.instance

View File

@ -919,6 +919,7 @@ Welcome to the wiki page!
"view_album",
"view_peoplepicturerelation",
"add_peoplepicturerelation",
"add_page",
]
)
)

View File

@ -30,7 +30,7 @@ export const paginated = async <T>(
endpoint: PaginatedEndpoint<T>,
options?: PaginatedRequest,
): Promise<T[]> => {
const maxPerPage = 199;
const maxPerPage = 200;
const queryParams = options ?? ({} as PaginatedRequest);
queryParams.query = queryParams.query ?? {};
queryParams.query.page_size = maxPerPage;

View File

@ -251,21 +251,31 @@ $hovered-red-text-color: #ff4d4d;
justify-content: flex-start;
}
>a {
a, button {
font-size: 100%;
margin: 0;
text-align: right;
color: $text-color;
&:hover {
color: $hovered-text-color;
}
}
&:last-child {
color: $red-text-color;
form#logout-form {
margin: 0;
display: inline;
}
#logout-form button {
color: $red-text-color;
&:hover {
color: $hovered-red-text-color;
}
&:hover {
color: $hovered-red-text-color;
}
background: none;
border: none;
cursor: pointer;
padding: 0;
}
}
}

View File

@ -84,18 +84,18 @@
</ul>
<div id="content">
{% block tabs %}
{%- block tabs -%}
{% include "core/base/tabs.jinja" %}
{% endblock %}
{%- endblock -%}
{% block errors%}
{%- block errors -%}
{% if error %}
{{ error }}
{% endif %}
{% endblock %}
{%- endblock -%}
{% block content %}
{% endblock %}
{%- block content -%}
{%- endblock -%}
</div>
</div>

View File

@ -59,7 +59,10 @@
</div>
<div class="links">
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
<form id="logout-form" method="post" action="{{ url("core:logout") }}">
{% csrf_token %}
<button type="submit">{% trans %}Logout{% endtrans %}</button>
</form>
</div>
</div>
<a

View File

@ -12,16 +12,15 @@
{% endif %}
{% endblock %}
{% macro print_page_name(page) %}
{% if page %}
{%- macro print_page_name(page) -%}
{%- if page -%}
{{ print_page_name(page.parent) }} >
<a href="{{ url('core:page', page_name=page.get_full_name()) }}">{{ page.get_display_name() }}</a>
{% endif %}
{% endmacro %}
{%- endif -%}
{%- endmacro -%}
{% block content %}
{{ print_page_name(page) }}
<div class="tool_bar">
<div class="tools">
{% if page %}

View File

@ -18,7 +18,9 @@ from smtplib import SMTPException
import freezegun
import pytest
from bs4 import BeautifulSoup
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import Permission
from django.core import mail
from django.core.cache import cache
from django.core.mail import EmailMessage
@ -223,17 +225,19 @@ def test_full_markdown_syntax():
class TestPageHandling(TestCase):
@classmethod
def setUpTestData(cls):
cls.root = User.objects.get(username="root")
cls.root_group = Group.objects.get(name="Root")
cls.group = baker.make(
Group, permissions=[Permission.objects.get(codename="add_page")]
)
cls.user = baker.make(User, groups=[cls.group])
def setUp(self):
self.client.force_login(self.root)
self.client.force_login(self.user)
def test_create_page_ok(self):
"""Should create a page correctly."""
response = self.client.post(
reverse("core:page_new"),
{"parent": "", "name": "guy", "owner_group": self.root_group.id},
{"parent": "", "name": "guy", "owner_group": self.group.id},
)
self.assertRedirects(
response, reverse("core:page", kwargs={"page_name": "guy"})
@ -249,32 +253,38 @@ class TestPageHandling(TestCase):
def test_create_child_page_ok(self):
"""Should create a page correctly."""
# remove all other pages to make sure there is no side effect
Page.objects.all().delete()
self.client.post(
reverse("core:page_new"),
{"parent": "", "name": "guy", "owner_group": str(self.root_group.id)},
parent = baker.prepare(Page)
parent.save(force_lock=True)
response = self.client.get(
reverse("core:page_new") + f"?page={parent._full_name}/new"
)
page = Page.objects.first()
self.client.post(
assert response.status_code == 200
# The name and parent inputs should be already filled
soup = BeautifulSoup(response.content.decode(), "lxml")
assert soup.find("input", {"name": "name"})["value"] == "new"
select = soup.find("autocomplete-select", {"name": "parent"})
assert select.find("option", {"selected": True})["value"] == str(parent.id)
response = self.client.post(
reverse("core:page_new"),
{
"parent": str(page.id),
"name": "bibou",
"owner_group": str(self.root_group.id),
"parent": str(parent.id),
"name": "new",
"owner_group": str(self.group.id),
},
)
response = self.client.get(
reverse("core:page", kwargs={"page_name": "guy/bibou"})
)
new_url = reverse("core:page", kwargs={"page_name": f"{parent._full_name}/new"})
assertRedirects(response, new_url, fetch_redirect_response=False)
response = self.client.get(new_url)
assert response.status_code == 200
assert '<a href="/page/guy/bibou/">' in str(response.content)
assert f'<a href="/page/{parent._full_name}/new/">' in response.content.decode()
def test_access_child_page_ok(self):
"""Should display a page correctly."""
parent = Page(name="guy", owner_group=self.root_group)
parent = Page(name="guy", owner_group=self.group)
parent.save(force_lock=True)
page = Page(name="bibou", owner_group=self.root_group, parent=parent)
page = Page(name="bibou", owner_group=self.group, parent=parent)
page.save(force_lock=True)
response = self.client.get(
reverse("core:page", kwargs={"page_name": "guy/bibou"})
@ -293,7 +303,8 @@ class TestPageHandling(TestCase):
def test_create_page_markdown_safe(self):
"""Should format the markdown and escape html correctly."""
self.client.post(
reverse("core:page_new"), {"parent": "", "name": "guy", "owner_group": "1"}
reverse("core:page_new"),
{"parent": "", "name": "guy", "owner_group": self.group.id},
)
self.client.post(
reverse("core:page_edit", kwargs={"page_name": "guy"}),

View File

@ -2,6 +2,7 @@ from datetime import timedelta
import pytest
from django.conf import settings
from django.contrib import auth
from django.core.management import call_command
from django.test import Client, TestCase
from django.urls import reverse
@ -219,3 +220,12 @@ def test_user_update_groups(client: Client):
manageable_groups[1],
*hidden_groups[:3],
}
@pytest.mark.django_db
def test_logout(client: Client):
user = baker.make(User)
client.force_login(user)
res = client.post(reverse("core:logout"))
assertRedirects(res, reverse("core:login"))
assert auth.get_user(client).is_anonymous

View File

@ -403,6 +403,7 @@ class FileModerationView(AllowFragment, ListView):
model = SithFile
template_name = "core/file_moderation.jinja"
queryset = SithFile.objects.filter(is_moderated=False, is_in_sas=False)
ordering = "id"
paginate_by = 100
def dispatch(self, request: HttpRequest, *args, **kwargs):

View File

@ -12,6 +12,7 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from django.contrib.auth.mixins import PermissionRequiredMixin
# This file contains all the views that concern the page model
from django.forms.models import modelform_factory
@ -22,7 +23,6 @@ from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from core.auth.mixins import (
CanCreateMixin,
CanEditMixin,
CanEditPropMixin,
CanViewMixin,
@ -115,20 +115,22 @@ class PageRevView(CanViewMixin, DetailView):
return context
class PageCreateView(CanCreateMixin, CreateView):
class PageCreateView(PermissionRequiredMixin, CreateView):
model = Page
form_class = PageForm
template_name = "core/page_prop.jinja"
permission_required = "core.add_page"
def get_initial(self):
init = {}
if "page" in self.request.GET:
page_name = self.request.GET["page"]
parent_name = "/".join(page_name.split("/")[:-1])
parent = Page.get_page_by_full_name(parent_name)
init = super().get_initial()
if "page" not in self.request.GET:
return init
page_name = self.request.GET["page"].rsplit("/", maxsplit=1)
if len(page_name) == 2:
parent = Page.get_page_by_full_name(page_name[0])
if parent is not None:
init["parent"] = parent.id
init["name"] = page_name.split("/")[-1]
init["name"] = page_name[-1]
return init
def get_context_data(self, **kwargs):

View File

@ -74,6 +74,8 @@ tests = [
"pytest-cov<7.0.0,>=6.0.0",
"pytest-django<5.0.0,>=4.10.0",
"model-bakery<2.0.0,>=1.20.4",
"beautifulsoup4>=4.13.3,<5",
"lxml>=5.3.1,<6",
]
docs = [
"mkdocs<2.0.0,>=1.6.1",

View File

@ -94,6 +94,13 @@ class SubscriptionNewUserForm(SubscriptionForm):
return email
def clean(self) -> dict[str, Any]:
"""Initialize the [User][core.models.User] linked to this subscription.
Warning:
The `User` is initialized, but not saved.
Don't use it for operations that expect
a persisted object.
"""
member = User(
first_name=self.cleaned_data.get("first_name"),
last_name=self.cleaned_data.get("last_name"),

View File

@ -92,9 +92,13 @@ class Subscription(models.Model):
self.member.make_home()
def get_absolute_url(self):
return reverse("core:user_edit", kwargs={"user_id": self.member.pk})
return reverse("core:user_edit", kwargs={"user_id": self.member_id})
def clean(self):
if self.member._state.adding:
# if the user is being created, then it makes no sense
# to check if the user is already subscribed
return
today = localdate()
threshold = timedelta(weeks=settings.SITH_SUBSCRIPTION_END)
# a user may subscribe if :

68
uv.lock generated
View File

@ -60,6 +60,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336 },
]
[[package]]
name = "beautifulsoup4"
version = "4.13.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "soupsieve" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 },
]
[[package]]
name = "bracex"
version = "2.5.post1"
@ -755,6 +768,48 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/20/caf3c7cf2432d85263119798c45221ddf67bdd7dae8f626d14ff8db04040/libsass-0.23.0-cp38-abi3-win_amd64.whl", hash = "sha256:a2ec85d819f353cbe807432d7275d653710d12b08ec7ef61c124a580a8352f3c", size = 872914 },
]
[[package]]
name = "lxml"
version = "5.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ef/f6/c15ca8e5646e937c148e147244817672cf920b56ac0bf2cc1512ae674be8/lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", size = 3678591 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/f4/5121aa9ee8e09b8b8a28cf3709552efe3d206ca51a20d6fa471b60bb3447/lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c", size = 8191889 },
{ url = "https://files.pythonhosted.org/packages/0a/ca/8e9aa01edddc74878f4aea85aa9ab64372f46aa804d1c36dda861bf9eabf/lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe", size = 4450685 },
{ url = "https://files.pythonhosted.org/packages/b2/b3/ea40a5c98619fbd7e9349df7007994506d396b97620ced34e4e5053d3734/lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9", size = 5051722 },
{ url = "https://files.pythonhosted.org/packages/3a/5e/375418be35f8a695cadfe7e7412f16520e62e24952ed93c64c9554755464/lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a", size = 4786661 },
{ url = "https://files.pythonhosted.org/packages/79/7c/d258eaaa9560f6664f9b426a5165103015bee6512d8931e17342278bad0a/lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0", size = 5311766 },
{ url = "https://files.pythonhosted.org/packages/03/bc/a041415be4135a1b3fdf017a5d873244cc16689456166fbdec4b27fba153/lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7", size = 4836014 },
{ url = "https://files.pythonhosted.org/packages/32/88/047f24967d5e3fc97848ea2c207eeef0f16239cdc47368c8b95a8dc93a33/lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae", size = 4961064 },
{ url = "https://files.pythonhosted.org/packages/3d/b5/ecf5a20937ecd21af02c5374020f4e3a3538e10a32379a7553fca3d77094/lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519", size = 4778341 },
{ url = "https://files.pythonhosted.org/packages/a4/05/56c359e07275911ed5f35ab1d63c8cd3360d395fb91e43927a2ae90b0322/lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322", size = 5345450 },
{ url = "https://files.pythonhosted.org/packages/b7/f4/f95e3ae12e9f32fbcde00f9affa6b0df07f495117f62dbb796a9a31c84d6/lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468", size = 4908336 },
{ url = "https://files.pythonhosted.org/packages/c5/f8/309546aec092434166a6e11c7dcecb5c2d0a787c18c072d61e18da9eba57/lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367", size = 4986049 },
{ url = "https://files.pythonhosted.org/packages/71/1c/b951817cb5058ca7c332d012dfe8bc59dabd0f0a8911ddd7b7ea8e41cfbd/lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd", size = 4860351 },
{ url = "https://files.pythonhosted.org/packages/31/23/45feba8dae1d35fcca1e51b051f59dc4223cbd23e071a31e25f3f73938a8/lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c", size = 5421580 },
{ url = "https://files.pythonhosted.org/packages/61/69/be245d7b2dbef81c542af59c97fcd641fbf45accf2dc1c325bae7d0d014c/lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f", size = 5285778 },
{ url = "https://files.pythonhosted.org/packages/69/06/128af2ed04bac99b8f83becfb74c480f1aa18407b5c329fad457e08a1bf4/lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645", size = 5054455 },
{ url = "https://files.pythonhosted.org/packages/8a/2d/f03a21cf6cc75cdd083563e509c7b6b159d761115c4142abb5481094ed8c/lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5", size = 3486315 },
{ url = "https://files.pythonhosted.org/packages/2b/9c/8abe21585d20ef70ad9cec7562da4332b764ed69ec29b7389d23dfabcea0/lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf", size = 3816925 },
{ url = "https://files.pythonhosted.org/packages/94/1c/724931daa1ace168e0237b929e44062545bf1551974102a5762c349c668d/lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e", size = 8171881 },
{ url = "https://files.pythonhosted.org/packages/67/0c/857b8fb6010c4246e66abeebb8639eaabba60a6d9b7c606554ecc5cbf1ee/lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd", size = 4440394 },
{ url = "https://files.pythonhosted.org/packages/61/72/c9e81de6a000f9682ccdd13503db26e973b24c68ac45a7029173237e3eed/lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7", size = 5037860 },
{ url = "https://files.pythonhosted.org/packages/24/26/942048c4b14835711b583b48cd7209bd2b5f0b6939ceed2381a494138b14/lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414", size = 4782513 },
{ url = "https://files.pythonhosted.org/packages/e2/65/27792339caf00f610cc5be32b940ba1e3009b7054feb0c4527cebac228d4/lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e", size = 5305227 },
{ url = "https://files.pythonhosted.org/packages/18/e1/25f7aa434a4d0d8e8420580af05ea49c3e12db6d297cf5435ac0a054df56/lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1", size = 4829846 },
{ url = "https://files.pythonhosted.org/packages/fe/ed/faf235e0792547d24f61ee1448159325448a7e4f2ab706503049d8e5df19/lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5", size = 4949495 },
{ url = "https://files.pythonhosted.org/packages/e5/e1/8f572ad9ed6039ba30f26dd4c2c58fb90f79362d2ee35ca3820284767672/lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423", size = 4773415 },
{ url = "https://files.pythonhosted.org/packages/a3/75/6b57166b9d1983dac8f28f354e38bff8d6bcab013a241989c4d54c72701b/lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20", size = 5337710 },
{ url = "https://files.pythonhosted.org/packages/cc/71/4aa56e2daa83bbcc66ca27b5155be2f900d996f5d0c51078eaaac8df9547/lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8", size = 4897362 },
{ url = "https://files.pythonhosted.org/packages/65/10/3fa2da152cd9b49332fd23356ed7643c9b74cad636ddd5b2400a9730d12b/lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9", size = 4977795 },
{ url = "https://files.pythonhosted.org/packages/de/d2/e1da0f7b20827e7b0ce934963cb6334c1b02cf1bb4aecd218c4496880cb3/lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c", size = 4858104 },
{ url = "https://files.pythonhosted.org/packages/a5/35/063420e1b33d3308f5aa7fcbdd19ef6c036f741c9a7a4bd5dc8032486b27/lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b", size = 5416531 },
{ url = "https://files.pythonhosted.org/packages/c3/83/93a6457d291d1e37adfb54df23498101a4701834258c840381dd2f6a030e/lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5", size = 5273040 },
{ url = "https://files.pythonhosted.org/packages/39/25/ad4ac8fac488505a2702656550e63c2a8db3a4fd63db82a20dad5689cecb/lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252", size = 5050951 },
{ url = "https://files.pythonhosted.org/packages/82/74/f7d223c704c87e44b3d27b5e0dde173a2fcf2e89c0524c8015c2b3554876/lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78", size = 3485357 },
{ url = "https://files.pythonhosted.org/packages/80/83/8c54533b3576f4391eebea88454738978669a6cad0d8e23266224007939d/lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332", size = 3814484 },
]
[[package]]
name = "markdown"
version = "3.7"
@ -1560,7 +1615,9 @@ prod = [
{ name = "psycopg", extra = ["c"] },
]
tests = [
{ name = "beautifulsoup4" },
{ name = "freezegun" },
{ name = "lxml" },
{ name = "model-bakery" },
{ name = "pytest" },
{ name = "pytest-cov" },
@ -1620,7 +1677,9 @@ docs = [
]
prod = [{ name = "psycopg", extras = ["c"], specifier = ">=3.2.3,<4.0.0" }]
tests = [
{ name = "beautifulsoup4", specifier = ">=4.13.3,<5" },
{ name = "freezegun", specifier = ">=1.5.1,<2.0.0" },
{ name = "lxml", specifier = ">=5.3.1,<6" },
{ name = "model-bakery", specifier = ">=1.20.4,<2.0.0" },
{ name = "pytest", specifier = ">=8.3.5,<9.0.0" },
{ name = "pytest-cov", specifier = ">=6.0.0,<7.0.0" },
@ -1645,6 +1704,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 },
]
[[package]]
name = "soupsieve"
version = "2.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
]
[[package]]
name = "sphinx"
version = "5.3.0"