mirror of
https://github.com/ae-utbm/sith.git
synced 2025-12-16 04:43:28 +00:00
Compare commits
28 Commits
dependence
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af11cbdc96 | ||
|
|
d3edcaff14 | ||
|
|
8c127a96f7 | ||
|
|
55d6e2bbec | ||
|
|
e9fbac8264 | ||
|
|
1911f2e6dd | ||
|
|
77bdc8dcb5 | ||
|
|
00acdcd1a5 | ||
|
|
aa77cfd1c8 | ||
|
|
0d4b77ba1c | ||
|
|
5271783e88 | ||
|
|
4ff4d179a1 | ||
|
|
7cbb3a2c5d | ||
|
|
a0768d6d7f | ||
|
|
f55627a292 | ||
|
|
4f802ac56e | ||
|
|
16a6e07d4b | ||
|
|
33d6300131 | ||
|
|
6709befb1f | ||
|
|
ddfb88ca2a | ||
|
|
acdb9660f6 | ||
|
|
b60bd3a42b | ||
|
|
0c046b6164 | ||
|
|
c588e5117d | ||
|
|
ad87617018 | ||
|
|
56c2c2b70e | ||
|
|
78fe4e52ca | ||
|
|
2a5893aa79 |
@@ -1,18 +1,16 @@
|
||||
from typing import Annotated
|
||||
|
||||
from annotated_types import MinLen
|
||||
from django.db.models import Q
|
||||
from ninja import Field, FilterSchema, ModelSchema
|
||||
from ninja import FilterLookup, FilterSchema, ModelSchema
|
||||
|
||||
from club.models import Club, Membership
|
||||
from core.schemas import SimpleUserSchema
|
||||
from core.schemas import NonEmptyStr, SimpleUserSchema
|
||||
|
||||
|
||||
class ClubSearchFilterSchema(FilterSchema):
|
||||
search: Annotated[str, MinLen(1)] | None = Field(None, q="name__icontains")
|
||||
search: Annotated[NonEmptyStr | None, FilterLookup("name__icontains")] = None
|
||||
is_active: bool | None = None
|
||||
parent_id: int | None = None
|
||||
parent_name: str | None = Field(None, q="parent__name__icontains")
|
||||
exclude_ids: set[int] | None = None
|
||||
|
||||
def filter_exclude_ids(self, value: set[int] | None):
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from ninja import FilterSchema, ModelSchema
|
||||
from ninja import FilterLookup, FilterSchema, ModelSchema
|
||||
from ninja_extra import service_resolver
|
||||
from ninja_extra.context import RouteContext
|
||||
from pydantic import Field
|
||||
|
||||
from club.schemas import ClubProfileSchema
|
||||
from com.models import News, NewsDate
|
||||
@@ -11,12 +11,12 @@ from core.markdown import markdown
|
||||
|
||||
|
||||
class NewsDateFilterSchema(FilterSchema):
|
||||
before: datetime | None = Field(None, q="end_date__lt")
|
||||
after: datetime | None = Field(None, q="start_date__gt")
|
||||
club_id: int | None = Field(None, q="news__club_id")
|
||||
before: Annotated[datetime | None, FilterLookup("end_date__lt")] = None
|
||||
after: Annotated[datetime | None, FilterLookup("start_date__gt")] = None
|
||||
club_id: Annotated[int | None, FilterLookup("news__club_id")] = None
|
||||
news_id: int | None = None
|
||||
is_published: bool | None = Field(None, q="news__is_published")
|
||||
title: str | None = Field(None, q="news__title__icontains")
|
||||
is_published: Annotated[bool | None, FilterLookup("news__is_published")] = None
|
||||
title: Annotated[str | None, FilterLookup("news__title__icontains")] = None
|
||||
|
||||
|
||||
class NewsSchema(ModelSchema):
|
||||
|
||||
@@ -350,7 +350,6 @@ class Command(BaseCommand):
|
||||
date=make_aware(
|
||||
self.faker.date_time_between(customer.since, localdate())
|
||||
),
|
||||
is_validated=True,
|
||||
)
|
||||
)
|
||||
sales.extend(this_customer_sales)
|
||||
|
||||
@@ -38,7 +38,6 @@ from django.contrib.auth.models import AnonymousUser as AuthAnonymousUser
|
||||
from django.contrib.auth.models import Group as AuthGroup
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core import validators
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.files import File
|
||||
from django.core.files.base import ContentFile
|
||||
@@ -77,16 +76,6 @@ class Group(AuthGroup):
|
||||
def get_absolute_url(self) -> str:
|
||||
return reverse("core:group_list")
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
super().save(*args, **kwargs)
|
||||
cache.set(f"sith_group_{self.id}", self)
|
||||
cache.set(f"sith_group_{self.name.replace(' ', '_')}", self)
|
||||
|
||||
def delete(self, *args, **kwargs) -> None:
|
||||
super().delete(*args, **kwargs)
|
||||
cache.delete(f"sith_group_{self.id}")
|
||||
cache.delete(f"sith_group_{self.name.replace(' ', '_')}")
|
||||
|
||||
|
||||
def validate_promo(value: int) -> None:
|
||||
last_promo = get_last_promo()
|
||||
|
||||
@@ -15,6 +15,8 @@ from pydantic_core.core_schema import ValidationInfo
|
||||
from core.models import Group, QuickUploadImage, SithFile, User
|
||||
from core.utils import is_image
|
||||
|
||||
NonEmptyStr = Annotated[str, MinLen(1)]
|
||||
|
||||
|
||||
class UploadedImage(UploadedFile):
|
||||
@classmethod
|
||||
|
||||
@@ -195,18 +195,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.delete {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: orangered;
|
||||
form .link-like {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: orangered;
|
||||
|
||||
@media (max-width: 375px) {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
@media (max-width: 375px) {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,12 +78,6 @@
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro delete_godfather(user, profile, godfather, is_father) %}
|
||||
{% if user == profile or user.is_root or user.is_board_member %}
|
||||
<a class="delete" href="{{ url("core:user_godfathers_delete", user_id=profile.id, godfather_id=godfather.id, is_father=is_father) }}">{% trans %}Delete{% endtrans %}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro paginate_alpine(page, nb_pages) %}
|
||||
{# Add pagination buttons for ajax based content with alpine
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
|
||||
{% if target %}
|
||||
<p>{% trans user=target.get_display_name() %}Change password for {{ user }}{% endtrans %}</p>
|
||||
<p>{% trans user=form.user.get_display_name() %}Change password for {{ user }}{% endtrans %}</p>
|
||||
{% endif %}
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -9,19 +9,17 @@
|
||||
{% block content %}
|
||||
<h4>{% trans %}Users{% endtrans %}</h4>
|
||||
<ul>
|
||||
{% for i in result.users %}
|
||||
{% if user.can_view(i) %}
|
||||
<li>
|
||||
{{ user_link_with_pict(i) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for user in users %}
|
||||
<li>
|
||||
{{ user_link_with_pict(user) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h4>{% trans %}Clubs{% endtrans %}</h4>
|
||||
<ul>
|
||||
{% for i in result.clubs %}
|
||||
{% for club in clubs %}
|
||||
<li>
|
||||
<a href="{{ url("club:club_view", club_id=i.id) }}">{{ i }}</a>
|
||||
<a href="{{ url("club:club_view", club_id=club.id) }}">{{ club }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@@ -29,7 +29,16 @@
|
||||
<a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
|
||||
{{ u.get_mini_item() | safe }}
|
||||
</a>
|
||||
{{ delete_godfather(user, profile, u, True) }}
|
||||
{% if user == profile or user.is_root or user.is_board_member %}
|
||||
<form
|
||||
method="post"
|
||||
class="no-margin"
|
||||
action="{{ url("core:user_godfathers_delete", user_id=profile.id, godfather_id=u.id, is_father=True) }}"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="link-like" value="{% trans %}Delete{% endtrans %}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
@@ -46,7 +55,16 @@
|
||||
<a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
|
||||
{{ u.get_mini_item()|safe }}
|
||||
</a>
|
||||
{{ delete_godfather(user, profile, u, False) }}
|
||||
{% if user == profile or user.is_root or user.is_board_member %}
|
||||
<form
|
||||
method="post"
|
||||
class="no-margin"
|
||||
action="{{ url("core:user_godfathers_delete", user_id=profile.id, godfather_id=u.id, is_father=False) }}"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="link-like link-red" value="{% trans %}Delete{% endtrans %}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@@ -11,32 +11,35 @@
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% if profile.permanencies %}
|
||||
{% if total_perm_time %}
|
||||
<div>
|
||||
<h3>{% trans %}Permanencies{% endtrans %}</h3>
|
||||
<div class="flexed">
|
||||
<div><span>Foyer :</span><span>{{ total_foyer_time }}</span></div>
|
||||
<div><span>Gommette :</span><span>{{ total_gommette_time }}</span></div>
|
||||
<div><span>MDE :</span><span>{{ total_mde_time }}</span></div>
|
||||
<div><b>Total :</b><b>{{ total_perm_time }}</b></div>
|
||||
{% for perm in perm_time %}
|
||||
<div>
|
||||
<span>{{ perm["counter__name"] }} :</span>
|
||||
<span>{{ perm["total"]|format_timedelta }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div><b>Total :</b><b>{{ total_perm_time|format_timedelta }}</b></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<h3>{% trans %}Buyings{% endtrans %}</h3>
|
||||
<div class="flexed">
|
||||
<div><span>Foyer :</span><span>{{ total_foyer_buyings }} €</span></div>
|
||||
<div><span>Gommette :</span><span>{{ total_gommette_buyings }} €</span></div>
|
||||
<div><span>MDE :</span><span>{{ total_mde_buyings }} €</span></div>
|
||||
<div><b>Total :</b><b>{{ total_foyer_buyings + total_gommette_buyings + total_mde_buyings }} €</b>
|
||||
</div>
|
||||
{% for sum in purchase_sums %}
|
||||
<div>
|
||||
<span>{{ sum["counter__name"] }}</span>
|
||||
<span>{{ sum["total"] }} €</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div><b>Total : </b><b>{{ total_purchases }} €</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>{% trans %}Product top 10{% endtrans %}</h3>
|
||||
<h3>{% trans %}Product top 15{% endtrans %}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -55,31 +55,17 @@ def phonenumber(
|
||||
return value
|
||||
|
||||
|
||||
@register.filter(name="truncate_time")
|
||||
def truncate_time(value, time_unit):
|
||||
"""Remove everything in the time format lower than the specified unit.
|
||||
|
||||
Args:
|
||||
value: the value to truncate
|
||||
time_unit: the lowest unit to display
|
||||
"""
|
||||
value = str(value)
|
||||
return {
|
||||
"millis": lambda: value.split(".")[0],
|
||||
"seconds": lambda: value.rsplit(":", maxsplit=1)[0],
|
||||
"minutes": lambda: value.split(":", maxsplit=1)[0],
|
||||
"hours": lambda: value.rsplit(" ")[0],
|
||||
}[time_unit]()
|
||||
|
||||
|
||||
@register.filter(name="format_timedelta")
|
||||
def format_timedelta(value: datetime.timedelta) -> str:
|
||||
value = value - datetime.timedelta(microseconds=value.microseconds)
|
||||
days = value.days
|
||||
if days == 0:
|
||||
return str(value)
|
||||
remainder = value - datetime.timedelta(days=days)
|
||||
return ngettext(
|
||||
"%(nb_days)d day, %(remainder)s", "%(nb_days)d days, %(remainder)s", days
|
||||
"%(nb_days)d day, %(remainder)s",
|
||||
"%(nb_days)d days, %(remainder)s",
|
||||
days,
|
||||
) % {"nb_days": days, "remainder": str(remainder)}
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ from pytest_django.asserts import assertInHTML, assertRedirects
|
||||
|
||||
from antispam.models import ToxicDomain
|
||||
from club.models import Club, Membership
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.markdown import markdown
|
||||
from core.models import AnonymousUser, Group, Page, User, validate_promo
|
||||
from core.utils import get_last_promo, get_semester_code, get_start_of_semester
|
||||
@@ -551,3 +552,10 @@ def test_allow_fragment_mixin():
|
||||
assert not TestAllowFragmentView.as_view()(request)
|
||||
request.headers = {"HX-Request": True, **base_headers}
|
||||
assert TestAllowFragmentView.as_view()(request)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_search_view(client: Client):
|
||||
client.force_login(subscriber_user.make())
|
||||
response = client.get(reverse("core:search", query={"query": "foo"}))
|
||||
assert response.status_code == 200
|
||||
|
||||
64
core/tests/test_notification.py
Normal file
64
core/tests/test_notification.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from datetime import timedelta
|
||||
from operator import attrgetter
|
||||
|
||||
import pytest
|
||||
from bs4 import BeautifulSoup
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from model_bakery import baker, seq
|
||||
from pytest_django.asserts import assertRedirects
|
||||
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import Notification
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestNotificationList(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.user = subscriber_user.make()
|
||||
url = reverse("core:user_profile", kwargs={"user_id": cls.user.id})
|
||||
cls.notifs = baker.make(
|
||||
Notification,
|
||||
user=cls.user,
|
||||
url=url,
|
||||
viewed=False,
|
||||
date=seq(now() - timedelta(days=1), timedelta(hours=1)),
|
||||
_quantity=10,
|
||||
_bulk_create=True,
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse("core:notification_list"))
|
||||
assert response.status_code == 200
|
||||
soup = BeautifulSoup(response.text, "lxml")
|
||||
ul = soup.find("ul", id="notifications")
|
||||
elements = list(ul.find_all("li"))
|
||||
assert len(elements) == len(self.notifs)
|
||||
notifs = sorted(self.notifs, key=attrgetter("date"), reverse=True)
|
||||
for element, notif in zip(elements, notifs, strict=True):
|
||||
assert element.find("a")["href"] == reverse(
|
||||
"core:notification", kwargs={"notif_id": notif.id}
|
||||
)
|
||||
|
||||
def test_read_all(self):
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(
|
||||
reverse("core:notification_list", query={"read_all": None})
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert not self.user.notifications.filter(viewed=True).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_notification_redirect(client: Client):
|
||||
user = subscriber_user.make()
|
||||
url = reverse("core:user_profile", kwargs={"user_id": user.id})
|
||||
notif = baker.make(Notification, user=user, url=url, viewed=False)
|
||||
client.force_login(user)
|
||||
response = client.get(reverse("core:notification", kwargs={"notif_id": notif.id}))
|
||||
assertRedirects(response, url)
|
||||
notif.refresh_from_db()
|
||||
assert notif.viewed is True
|
||||
@@ -1,3 +1,4 @@
|
||||
import itertools
|
||||
from datetime import timedelta
|
||||
from unittest import mock
|
||||
|
||||
@@ -23,7 +24,7 @@ from core.baker_recipes import (
|
||||
from core.models import AnonymousUser, Group, User
|
||||
from core.views import UserTabsMixin
|
||||
from counter.baker_recipes import sale_recipe
|
||||
from counter.models import Counter, Customer, Refilling, Selling
|
||||
from counter.models import Counter, Customer, Permanency, Refilling, Selling
|
||||
from counter.utils import is_logged_in_counter
|
||||
from eboutic.models import Invoice, InvoiceItem
|
||||
|
||||
@@ -187,11 +188,7 @@ class TestFilterInactive(TestCase):
|
||||
time_inactive = time_active - timedelta(days=3)
|
||||
counter, seller = baker.make(Counter), baker.make(User)
|
||||
sale_recipe = Recipe(
|
||||
Selling,
|
||||
counter=counter,
|
||||
club=counter.club,
|
||||
seller=seller,
|
||||
is_validated=True,
|
||||
Selling, counter=counter, club=counter.club, seller=seller, unit_price=0
|
||||
)
|
||||
|
||||
cls.users = [
|
||||
@@ -428,3 +425,106 @@ class TestUserQuerySetViewableBy:
|
||||
user = user_factory()
|
||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||
assert not viewable.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_preferences(client: Client):
|
||||
user = subscriber_user.make()
|
||||
client.force_login(user)
|
||||
url = reverse("core:user_prefs", kwargs={"user_id": user.id})
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
response = client.post(url, {"notify_on_click": "true"})
|
||||
assertRedirects(response, url)
|
||||
user.preferences.refresh_from_db()
|
||||
assert user.preferences.notify_on_click is True
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_stats(client: Client):
|
||||
user = subscriber_user.make()
|
||||
baker.make(Refilling, customer=user.customer, amount=99999)
|
||||
bars = [b[0] for b in settings.SITH_COUNTER_BARS]
|
||||
baker.make(
|
||||
Permanency,
|
||||
end=now() - timedelta(days=5),
|
||||
start=now() - timedelta(days=5, hours=3),
|
||||
counter_id=itertools.cycle(bars),
|
||||
_quantity=5,
|
||||
_bulk_create=True,
|
||||
)
|
||||
sale_recipe.make(
|
||||
counter_id=itertools.cycle(bars),
|
||||
customer=user.customer,
|
||||
unit_price=1,
|
||||
quantity=1,
|
||||
_quantity=5,
|
||||
)
|
||||
client.force_login(user)
|
||||
response = client.get(reverse("core:user_stats", kwargs={"user_id": user.id}))
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestChangeUserPassword:
|
||||
def test_as_root(self, client: Client, admin_user: User):
|
||||
client.force_login(admin_user)
|
||||
user = subscriber_user.make()
|
||||
url = reverse("core:password_root_change", kwargs={"user_id": user.id})
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
response = client.post(
|
||||
url, {"new_password1": "poutou", "new_password2": "poutou"}
|
||||
)
|
||||
assertRedirects(response, reverse("core:password_change_done"))
|
||||
user.refresh_from_db()
|
||||
assert user.check_password("poutou") is True
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestUserGodfather:
|
||||
@pytest.mark.parametrize("godfather", [True, False])
|
||||
def test_add_family(self, client: Client, godfather):
|
||||
user = subscriber_user.make()
|
||||
other_user = subscriber_user.make()
|
||||
client.force_login(user)
|
||||
url = reverse("core:user_godfathers", kwargs={"user_id": user.id})
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
response = client.post(
|
||||
url,
|
||||
{"type": "godfather" if godfather else "godchild", "user": other_user.id},
|
||||
)
|
||||
assertRedirects(response, url)
|
||||
if godfather:
|
||||
assert user.godfathers.contains(other_user)
|
||||
else:
|
||||
assert user.godchildren.contains(other_user)
|
||||
|
||||
def test_tree(self, client: Client):
|
||||
user = subscriber_user.make()
|
||||
client.force_login(user)
|
||||
response = client.get(
|
||||
reverse("core:user_godfathers_tree", kwargs={"user_id": user.id})
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_remove_family(self, client: Client):
|
||||
user = subscriber_user.make()
|
||||
other_user = subscriber_user.make()
|
||||
user.godfathers.add(other_user)
|
||||
client.force_login(user)
|
||||
response = client.post(
|
||||
reverse(
|
||||
"core:user_godfathers_delete",
|
||||
kwargs={
|
||||
"user_id": user.id,
|
||||
"godfather_id": other_user.id,
|
||||
"is_father": True,
|
||||
},
|
||||
)
|
||||
)
|
||||
assertRedirects(
|
||||
response, reverse("core:user_godfathers", kwargs={"user_id": user.id})
|
||||
)
|
||||
assert not user.godfathers.contains(other_user)
|
||||
|
||||
16
core/urls.py
16
core/urls.py
@@ -24,6 +24,7 @@
|
||||
from django.urls import path, re_path, register_converter
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from com.views import NewsListView
|
||||
from core.converters import (
|
||||
BooleanStringConverter,
|
||||
FourDigitYearConverter,
|
||||
@@ -53,6 +54,8 @@ from core.views import (
|
||||
PagePropView,
|
||||
PageRevView,
|
||||
PageView,
|
||||
PasswordRootChangeView,
|
||||
SearchView,
|
||||
SithLoginView,
|
||||
SithPasswordChangeDoneView,
|
||||
SithPasswordChangeView,
|
||||
@@ -76,13 +79,8 @@ from core.views import (
|
||||
UserUpdateProfileView,
|
||||
UserView,
|
||||
delete_user_godfather,
|
||||
index,
|
||||
logout,
|
||||
notification,
|
||||
password_root_change,
|
||||
search_json,
|
||||
search_user_json,
|
||||
search_view,
|
||||
send_file,
|
||||
)
|
||||
|
||||
@@ -91,20 +89,18 @@ register_converter(TwoDigitMonthConverter, "mm")
|
||||
register_converter(BooleanStringConverter, "bool")
|
||||
|
||||
urlpatterns = [
|
||||
path("", index, name="index"),
|
||||
path("", NewsListView.as_view(), name="index"),
|
||||
path("notifications/", NotificationList.as_view(), name="notification_list"),
|
||||
path("notification/<int:notif_id>/", notification, name="notification"),
|
||||
# Search
|
||||
path("search/", search_view, name="search"),
|
||||
path("search_json/", search_json, name="search_json"),
|
||||
path("search_user/", search_user_json, name="search_user"),
|
||||
path("search/", SearchView.as_view(), name="search"),
|
||||
# Login and co
|
||||
path("login/", SithLoginView.as_view(), name="login"),
|
||||
path("logout/", logout, name="logout"),
|
||||
path("password_change/", SithPasswordChangeView.as_view(), name="password_change"),
|
||||
path(
|
||||
"password_change/<int:user_id>/",
|
||||
password_root_change,
|
||||
PasswordRootChangeView.as_view(),
|
||||
name="password_root_change",
|
||||
),
|
||||
path(
|
||||
|
||||
@@ -303,7 +303,6 @@ class UserGodfathersForm(forms.Form):
|
||||
)
|
||||
user = forms.ModelChoiceField(
|
||||
label=_("Select user"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
@@ -315,8 +314,6 @@ class UserGodfathersForm(forms.Form):
|
||||
|
||||
def clean_user(self):
|
||||
other_user = self.cleaned_data.get("user")
|
||||
if not other_user:
|
||||
raise ValidationError(_("This user does not exist"))
|
||||
if other_user == self.target_user:
|
||||
raise ValidationError(_("You cannot be related to yourself"))
|
||||
return other_user
|
||||
|
||||
@@ -22,106 +22,49 @@
|
||||
#
|
||||
#
|
||||
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core import serializers
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import F
|
||||
from django.db.models.query import QuerySet
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils import html
|
||||
from django.utils.text import slugify
|
||||
from django.views.generic import ListView
|
||||
from haystack.query import SearchQuerySet
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.generic import ListView, TemplateView
|
||||
|
||||
from club.models import Club
|
||||
from core.models import Notification, User
|
||||
from core.schemas import UserFilterSchema
|
||||
|
||||
|
||||
def index(request, context=None):
|
||||
from com.views import NewsListView
|
||||
|
||||
return NewsListView.as_view()(request)
|
||||
|
||||
|
||||
class NotificationList(ListView):
|
||||
class NotificationList(LoginRequiredMixin, ListView):
|
||||
model = Notification
|
||||
template_name = "core/notification_list.jinja"
|
||||
|
||||
def get_queryset(self) -> QuerySet[Notification]:
|
||||
if self.request.user.is_anonymous:
|
||||
return Notification.objects.none()
|
||||
# TODO: Bulk update in django 2.2
|
||||
if "see_all" in self.request.GET:
|
||||
self.request.user.notifications.filter(viewed=False).update(viewed=True)
|
||||
return self.request.user.notifications.order_by("-date")[:20]
|
||||
|
||||
|
||||
def notification(request, notif_id):
|
||||
notif = Notification.objects.filter(id=notif_id).first()
|
||||
if notif:
|
||||
if notif.type not in settings.SITH_PERMANENT_NOTIFICATIONS:
|
||||
notif.viewed = True
|
||||
else:
|
||||
notif.callback()
|
||||
notif.save()
|
||||
return redirect(notif.url)
|
||||
return redirect("/")
|
||||
def notification(request: HttpRequest, notif_id: int):
|
||||
notif = get_object_or_404(Notification, id=notif_id)
|
||||
if notif.type not in settings.SITH_PERMANENT_NOTIFICATIONS:
|
||||
notif.viewed = True
|
||||
else:
|
||||
notif.callback()
|
||||
notif.save()
|
||||
return redirect(notif.url)
|
||||
|
||||
|
||||
def search_user(query):
|
||||
try:
|
||||
# slugify turns everything into ascii and every whitespace into -
|
||||
# it ends by removing duplicate - (so ' - ' will turn into '-')
|
||||
# replace('-', ' ') because search is whitespace based
|
||||
query = slugify(query).replace("-", " ")
|
||||
# TODO: is this necessary?
|
||||
query = html.escape(query)
|
||||
res = (
|
||||
SearchQuerySet()
|
||||
.models(User)
|
||||
.autocomplete(auto=query)
|
||||
.order_by("-last_login")
|
||||
.load_all()[:20]
|
||||
)
|
||||
return [r.object for r in res]
|
||||
except TypeError:
|
||||
return []
|
||||
class SearchView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "core/search.jinja"
|
||||
|
||||
|
||||
def search_club(query, *, as_json=False):
|
||||
clubs = []
|
||||
if query:
|
||||
clubs = Club.objects.filter(name__icontains=query).all()
|
||||
clubs = clubs[:5]
|
||||
if as_json:
|
||||
# Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
|
||||
clubs = json.loads(serializers.serialize("json", clubs, fields=("name")))
|
||||
else:
|
||||
clubs = list(clubs)
|
||||
return clubs
|
||||
|
||||
|
||||
@login_required
|
||||
def search_view(request):
|
||||
result = {
|
||||
"users": search_user(request.GET.get("query", "")),
|
||||
"clubs": search_club(request.GET.get("query", "")),
|
||||
}
|
||||
return render(request, "core/search.jinja", context={"result": result})
|
||||
|
||||
|
||||
@login_required
|
||||
def search_user_json(request):
|
||||
result = {"users": search_user(request.GET.get("query", ""))}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
||||
@login_required
|
||||
def search_json(request):
|
||||
result = {
|
||||
"users": search_user(request.GET.get("query", "")),
|
||||
"clubs": search_club(request.GET.get("query", ""), as_json=True),
|
||||
}
|
||||
return JsonResponse(result)
|
||||
def get_context_data(self, **kwargs):
|
||||
users, clubs = [], []
|
||||
if query := self.request.GET.get("query"):
|
||||
users = list(
|
||||
UserFilterSchema(search=query)
|
||||
.filter(User.objects.viewable_by(self.request.user))
|
||||
.order_by(F("last_login").desc(nulls_last=True))
|
||||
)
|
||||
clubs = list(Club.objects.filter(name__icontains=query)[:5])
|
||||
return super().get_context_data(**kwargs) | {"users": users, "clubs": clubs}
|
||||
|
||||
@@ -22,27 +22,28 @@
|
||||
#
|
||||
#
|
||||
import itertools
|
||||
from datetime import timedelta
|
||||
|
||||
# This file contains all the views that concern the user model
|
||||
from datetime import date, timedelta
|
||||
from operator import itemgetter
|
||||
from smtplib import SMTPException
|
||||
|
||||
from django.contrib.auth import login, views
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import DateField, QuerySet
|
||||
from django.db.models import DateField, F, QuerySet, Sum
|
||||
from django.db.models.functions import Trunc
|
||||
from django.forms.models import modelform_factory
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.safestring import SafeString
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.generic import (
|
||||
CreateView,
|
||||
DeleteView,
|
||||
@@ -66,9 +67,8 @@ from core.views.forms import (
|
||||
UserProfileForm,
|
||||
)
|
||||
from core.views.mixins import TabedViewMixin, UseFragmentsMixin
|
||||
from counter.models import Counter, Refilling, Selling
|
||||
from counter.models import Refilling, Selling
|
||||
from eboutic.models import Invoice
|
||||
from subscription.models import Subscription
|
||||
from trombi.views import UserTrombiForm
|
||||
|
||||
|
||||
@@ -99,21 +99,23 @@ def logout(request):
|
||||
return views.logout_then_login(request)
|
||||
|
||||
|
||||
def password_root_change(request, user_id):
|
||||
class PasswordRootChangeView(UserPassesTestMixin, FormView):
|
||||
"""Allows a root user to change someone's password."""
|
||||
if not request.user.is_root:
|
||||
raise PermissionDenied
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
if request.method == "POST":
|
||||
form = views.SetPasswordForm(user=user, data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect("core:password_change_done")
|
||||
else:
|
||||
form = views.SetPasswordForm(user=user)
|
||||
return TemplateResponse(
|
||||
request, "core/password_change.jinja", {"form": form, "target": user}
|
||||
)
|
||||
|
||||
template_name = "core/password_change.jinja"
|
||||
form_class = SetPasswordForm
|
||||
success_url = reverse_lazy("core:password_change_done")
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.is_root
|
||||
|
||||
def get_form_kwargs(self):
|
||||
user = get_object_or_404(User, id=self.kwargs["user_id"])
|
||||
return super().get_form_kwargs() | {"user": user}
|
||||
|
||||
def form_valid(self, form: SetPasswordForm):
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@method_decorator(check_honeypot, name="post")
|
||||
@@ -288,10 +290,12 @@ class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
return kwargs
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def delete_user_godfather(request, user_id, godfather_id, is_father):
|
||||
user_is_admin = request.user.is_root or request.user.is_board_member
|
||||
if user_id != request.user.id and not user_is_admin:
|
||||
raise PermissionDenied()
|
||||
raise PermissionDenied
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
to_remove = get_object_or_404(User, id=godfather_id)
|
||||
if is_father:
|
||||
@@ -353,87 +357,40 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
context_object_name = "profile"
|
||||
template_name = "core/user_stats.jinja"
|
||||
current_tab = "stats"
|
||||
queryset = User.objects.exclude(customer=None).select_related("customer")
|
||||
|
||||
def dispatch(self, request, *arg, **kwargs):
|
||||
profile = self.get_object()
|
||||
|
||||
if not hasattr(profile, "customer"):
|
||||
raise Http404
|
||||
|
||||
if not (
|
||||
profile == request.user or request.user.has_perm("counter.view_customer")
|
||||
):
|
||||
raise PermissionDenied
|
||||
|
||||
return super().dispatch(request, *arg, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
from django.db.models import Sum
|
||||
|
||||
foyer = Counter.objects.filter(name="Foyer").first()
|
||||
mde = Counter.objects.filter(name="MDE").first()
|
||||
gommette = Counter.objects.filter(name="La Gommette").first()
|
||||
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
||||
kwargs["perm_time"] = list(
|
||||
self.object.permanencies.filter(end__isnull=False, counter__type="BAR")
|
||||
.values("counter", "counter__name")
|
||||
.annotate(total=Sum(F("end") - F("start"), default=timedelta(seconds=0)))
|
||||
.order_by("-total")
|
||||
)
|
||||
kwargs["total_perm_time"] = sum(
|
||||
[p.end - p.start for p in self.object.permanencies.exclude(end=None)],
|
||||
timedelta(),
|
||||
[perm["total"] for perm in kwargs["perm_time"]], start=timedelta(seconds=0)
|
||||
)
|
||||
kwargs["total_foyer_time"] = sum(
|
||||
[
|
||||
p.end - p.start
|
||||
for p in self.object.permanencies.filter(counter=foyer).exclude(
|
||||
end=None
|
||||
)
|
||||
],
|
||||
timedelta(),
|
||||
)
|
||||
kwargs["total_mde_time"] = sum(
|
||||
[
|
||||
p.end - p.start
|
||||
for p in self.object.permanencies.filter(counter=mde).exclude(end=None)
|
||||
],
|
||||
timedelta(),
|
||||
)
|
||||
kwargs["total_gommette_time"] = sum(
|
||||
[
|
||||
p.end - p.start
|
||||
for p in self.object.permanencies.filter(counter=gommette).exclude(
|
||||
end=None
|
||||
)
|
||||
],
|
||||
timedelta(),
|
||||
)
|
||||
kwargs["total_foyer_buyings"] = sum(
|
||||
[
|
||||
b.unit_price * b.quantity
|
||||
for b in self.object.customer.buyings.filter(
|
||||
counter=foyer, date__gte=semester_start
|
||||
)
|
||||
]
|
||||
)
|
||||
kwargs["total_mde_buyings"] = sum(
|
||||
[
|
||||
b.unit_price * b.quantity
|
||||
for b in self.object.customer.buyings.filter(
|
||||
counter=mde, date__gte=semester_start
|
||||
)
|
||||
]
|
||||
)
|
||||
kwargs["total_gommette_buyings"] = sum(
|
||||
[
|
||||
b.unit_price * b.quantity
|
||||
for b in self.object.customer.buyings.filter(
|
||||
counter=gommette, date__gte=semester_start
|
||||
)
|
||||
]
|
||||
kwargs["purchase_sums"] = list(
|
||||
self.object.customer.buyings.filter(counter__type="BAR")
|
||||
.values("counter", "counter__name")
|
||||
.annotate(total=Sum(F("unit_price") * F("quantity")))
|
||||
.order_by("-total")
|
||||
)
|
||||
kwargs["total_purchases"] = sum(s["total"] for s in kwargs["purchase_sums"])
|
||||
kwargs["top_product"] = (
|
||||
self.object.customer.buyings.values("product__name")
|
||||
.annotate(product_sum=Sum("quantity"))
|
||||
.exclude(product_sum=None)
|
||||
.order_by("-product_sum")
|
||||
.all()[:10]
|
||||
.all()[:15]
|
||||
)
|
||||
return kwargs
|
||||
|
||||
@@ -465,7 +422,6 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
form_class = UserProfileForm
|
||||
current_tab = "edit"
|
||||
edit_once = ["profile_pict", "date_of_birth", "first_name", "last_name"]
|
||||
board_only = []
|
||||
|
||||
def remove_restricted_fields(self, request):
|
||||
"""Removes edit_once and board_only fields."""
|
||||
@@ -474,9 +430,6 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
request.user.is_board_member or request.user.is_root
|
||||
):
|
||||
self.form.fields.pop(i, None)
|
||||
for i in self.board_only:
|
||||
if not (request.user.is_board_member or request.user.is_root):
|
||||
self.form.fields.pop(i, None)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@@ -528,10 +481,10 @@ class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, Update
|
||||
current_tab = "prefs"
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
pref = self.object.preferences
|
||||
kwargs.update({"instance": pref})
|
||||
return kwargs
|
||||
return super().get_form_kwargs() | {"instance": self.object.preferences}
|
||||
|
||||
def get_success_url(self):
|
||||
return self.request.path
|
||||
|
||||
def get_fragment_context_data(self) -> dict[str, SafeString]:
|
||||
# Avoid cyclic import error
|
||||
|
||||
@@ -24,12 +24,6 @@
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
PAYMENT_METHOD = [
|
||||
("CHECK", _("Check")),
|
||||
("CASH", _("Cash")),
|
||||
("CARD", _("Credit card")),
|
||||
]
|
||||
|
||||
|
||||
class CounterConfig(AppConfig):
|
||||
name = "counter"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import math
|
||||
import uuid
|
||||
from datetime import date
|
||||
from datetime import date, datetime, timezone
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django import forms
|
||||
@@ -136,7 +136,10 @@ class GetUserForm(forms.Form):
|
||||
|
||||
|
||||
class RefillForm(forms.ModelForm):
|
||||
allowed_refilling_methods = ["CASH", "CARD"]
|
||||
allowed_refilling_methods = [
|
||||
Refilling.PaymentMethod.CASH,
|
||||
Refilling.PaymentMethod.CARD,
|
||||
]
|
||||
|
||||
error_css_class = "error"
|
||||
required_css_class = "required"
|
||||
@@ -146,7 +149,7 @@ class RefillForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Refilling
|
||||
fields = ["amount", "payment_method", "bank"]
|
||||
fields = ["amount", "payment_method"]
|
||||
widgets = {"payment_method": forms.RadioSelect}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -160,9 +163,6 @@ class RefillForm(forms.ModelForm):
|
||||
if self.fields["payment_method"].initial not in self.allowed_refilling_methods:
|
||||
self.fields["payment_method"].initial = self.allowed_refilling_methods[0]
|
||||
|
||||
if "CHECK" not in self.allowed_refilling_methods:
|
||||
del self.fields["bank"]
|
||||
|
||||
|
||||
class CounterEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@@ -235,6 +235,19 @@ class ScheduledProductActionForm(forms.ModelForm):
|
||||
)
|
||||
return super().clean()
|
||||
|
||||
def set_product(self, product: Product):
|
||||
"""Set the product to which this form's instance is linked.
|
||||
|
||||
When this form is linked to a ProductForm in the case of a product's creation,
|
||||
the product doesn't exist yet, so saving this form as is will result
|
||||
in having `{"product_id": null}` in the action kwargs.
|
||||
For the creation to be useful, it may be needed to inject the newly created
|
||||
product into this form, before saving the latter.
|
||||
"""
|
||||
self.product = product
|
||||
kwargs = json.loads(self.instance.kwargs) | {"product_id": self.product.id}
|
||||
self.instance.kwargs = json.dumps(kwargs)
|
||||
|
||||
|
||||
class BaseScheduledProductActionFormSet(BaseModelFormSet):
|
||||
def __init__(self, *args, product: Product, **kwargs):
|
||||
@@ -321,11 +334,19 @@ class ProductForm(forms.ModelForm):
|
||||
def is_valid(self):
|
||||
return super().is_valid() and self.action_formset.is_valid()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
ret = super().save(*args, **kwargs)
|
||||
self.instance.counters.set(self.cleaned_data["counters"])
|
||||
def save(self, *args, **kwargs) -> Product:
|
||||
product = super().save(*args, **kwargs)
|
||||
product.counters.set(self.cleaned_data["counters"])
|
||||
for form in self.action_formset:
|
||||
# if it's a creation, the product given in the formset
|
||||
# wasn't a persisted instance.
|
||||
# So if we tried to persist the scheduled actions in the current state,
|
||||
# they would be linked to no product, thus be completely useless
|
||||
# To make it work, we have to replace
|
||||
# the initial product with a persisted one
|
||||
form.set_product(product)
|
||||
self.action_formset.save()
|
||||
return ret
|
||||
return product
|
||||
|
||||
|
||||
class ReturnableProductForm(forms.ModelForm):
|
||||
@@ -369,7 +390,6 @@ class EticketForm(forms.ModelForm):
|
||||
class CloseCustomerAccountForm(forms.Form):
|
||||
user = forms.ModelChoiceField(
|
||||
label=_("Refound this account"),
|
||||
help_text=None,
|
||||
required=True,
|
||||
widget=AutoCompleteSelectUser,
|
||||
queryset=User.objects.all(),
|
||||
@@ -489,13 +509,14 @@ class InvoiceCallForm(forms.Form):
|
||||
def __init__(self, *args, month: date, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.month = month
|
||||
month_start = datetime(month.year, month.month, month.day, tzinfo=timezone.utc)
|
||||
self.clubs = list(
|
||||
Club.objects.filter(
|
||||
Exists(
|
||||
Selling.objects.filter(
|
||||
club=OuterRef("pk"),
|
||||
date__gte=month,
|
||||
date__lte=month + relativedelta(months=1),
|
||||
date__gte=month_start,
|
||||
date__lte=month_start + relativedelta(months=1),
|
||||
)
|
||||
)
|
||||
).annotate(
|
||||
|
||||
@@ -119,7 +119,6 @@ class Command(BaseCommand):
|
||||
quantity=1,
|
||||
unit_price=account.amount,
|
||||
date=now(),
|
||||
is_validated=True,
|
||||
)
|
||||
for account in accounts
|
||||
]
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Generated by Django 5.2.8 on 2025-11-19 17:59
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.migrations.state import StateApps
|
||||
from django.db.models import Case, When
|
||||
|
||||
|
||||
def migrate_selling_payment_method(apps: StateApps, schema_editor):
|
||||
# 0 <=> SITH_ACCOUNT is the default value, so no need to migrate it
|
||||
Selling = apps.get_model("counter", "Selling")
|
||||
Selling.objects.filter(payment_method_str="CARD").update(payment_method=1)
|
||||
|
||||
|
||||
def migrate_selling_payment_method_reverse(apps: StateApps, schema_editor):
|
||||
Selling = apps.get_model("counter", "Selling")
|
||||
Selling.objects.filter(payment_method=1).update(payment_method_str="CARD")
|
||||
|
||||
|
||||
def migrate_refilling_payment_method(apps: StateApps, schema_editor):
|
||||
Refilling = apps.get_model("counter", "Refilling")
|
||||
Refilling.objects.update(
|
||||
payment_method=Case(
|
||||
When(payment_method_str="CARD", then=0),
|
||||
When(payment_method_str="CASH", then=1),
|
||||
When(payment_method_str="CHECK", then=2),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def migrate_refilling_payment_method_reverse(apps: StateApps, schema_editor):
|
||||
Refilling = apps.get_model("counter", "Refilling")
|
||||
Refilling.objects.update(
|
||||
payment_method_str=Case(
|
||||
When(payment_method=0, then="CARD"),
|
||||
When(payment_method=1, then="CASH"),
|
||||
When(payment_method=2, then="CHECK"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("counter", "0034_alter_selling_date_selling_date_month_idx")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name="selling", name="is_validated"),
|
||||
migrations.RemoveField(model_name="refilling", name="is_validated"),
|
||||
migrations.RemoveField(model_name="refilling", name="bank"),
|
||||
migrations.RenameField(
|
||||
model_name="selling",
|
||||
old_name="payment_method",
|
||||
new_name="payment_method_str",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="selling",
|
||||
name="payment_method",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
choices=[(0, "Sith account"), (1, "Credit card")],
|
||||
default=0,
|
||||
verbose_name="payment method",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
migrate_selling_payment_method, migrate_selling_payment_method_reverse
|
||||
),
|
||||
migrations.RemoveField(model_name="selling", name="payment_method_str"),
|
||||
migrations.RenameField(
|
||||
model_name="refilling",
|
||||
old_name="payment_method",
|
||||
new_name="payment_method_str",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="refilling",
|
||||
name="payment_method",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
choices=[(0, "Credit card"), (1, "Cash"), (2, "Check")],
|
||||
default=0,
|
||||
verbose_name="payment method",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
migrate_refilling_payment_method, migrate_refilling_payment_method_reverse
|
||||
),
|
||||
migrations.RemoveField(model_name="refilling", name="payment_method_str"),
|
||||
]
|
||||
@@ -44,7 +44,6 @@ from club.models import Club
|
||||
from core.fields import ResizedImageField
|
||||
from core.models import Group, Notification, User
|
||||
from core.utils import get_start_of_semester
|
||||
from counter.apps import PAYMENT_METHOD
|
||||
from counter.fields import CurrencyField
|
||||
from subscription.models import Subscription
|
||||
|
||||
@@ -80,7 +79,8 @@ class CustomerQuerySet(models.QuerySet):
|
||||
)
|
||||
money_out = Subquery(
|
||||
Selling.objects.filter(
|
||||
customer=OuterRef("pk"), payment_method="SITH_ACCOUNT"
|
||||
customer=OuterRef("pk"),
|
||||
payment_method=Selling.PaymentMethod.SITH_ACCOUNT,
|
||||
)
|
||||
.values("customer_id")
|
||||
.annotate(res=Sum(F("unit_price") * F("quantity"), default=0))
|
||||
@@ -731,6 +731,11 @@ class RefillingQuerySet(models.QuerySet):
|
||||
class Refilling(models.Model):
|
||||
"""Handle the refilling."""
|
||||
|
||||
class PaymentMethod(models.IntegerChoices):
|
||||
CARD = 0, _("Credit card")
|
||||
CASH = 1, _("Cash")
|
||||
CHECK = 2, _("Check")
|
||||
|
||||
counter = models.ForeignKey(
|
||||
Counter, related_name="refillings", blank=False, on_delete=models.CASCADE
|
||||
)
|
||||
@@ -745,16 +750,9 @@ class Refilling(models.Model):
|
||||
Customer, related_name="refillings", blank=False, on_delete=models.CASCADE
|
||||
)
|
||||
date = models.DateTimeField(_("date"))
|
||||
payment_method = models.CharField(
|
||||
_("payment method"),
|
||||
max_length=255,
|
||||
choices=PAYMENT_METHOD,
|
||||
default="CARD",
|
||||
payment_method = models.PositiveSmallIntegerField(
|
||||
_("payment method"), choices=PaymentMethod, default=PaymentMethod.CARD
|
||||
)
|
||||
bank = models.CharField(
|
||||
_("bank"), max_length=255, choices=settings.SITH_COUNTER_BANK, default="OTHER"
|
||||
)
|
||||
is_validated = models.BooleanField(_("is validated"), default=False)
|
||||
|
||||
objects = RefillingQuerySet.as_manager()
|
||||
|
||||
@@ -771,10 +769,9 @@ class Refilling(models.Model):
|
||||
if not self.date:
|
||||
self.date = timezone.now()
|
||||
self.full_clean()
|
||||
if not self.is_validated:
|
||||
if self._state.adding:
|
||||
self.customer.amount += self.amount
|
||||
self.customer.save()
|
||||
self.is_validated = True
|
||||
if self.customer.user.preferences.notify_on_refill:
|
||||
Notification(
|
||||
user=self.customer.user,
|
||||
@@ -814,6 +811,10 @@ class SellingQuerySet(models.QuerySet):
|
||||
class Selling(models.Model):
|
||||
"""Handle the sellings."""
|
||||
|
||||
class PaymentMethod(models.IntegerChoices):
|
||||
SITH_ACCOUNT = 0, _("Sith account")
|
||||
CARD = 1, _("Credit card")
|
||||
|
||||
# We make sure that sellings have a way begger label than any product name is allowed to
|
||||
label = models.CharField(_("label"), max_length=128)
|
||||
product = models.ForeignKey(
|
||||
@@ -850,13 +851,9 @@ class Selling(models.Model):
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
date = models.DateTimeField(_("date"), db_index=True)
|
||||
payment_method = models.CharField(
|
||||
_("payment method"),
|
||||
max_length=255,
|
||||
choices=[("SITH_ACCOUNT", _("Sith account")), ("CARD", _("Credit card"))],
|
||||
default="SITH_ACCOUNT",
|
||||
payment_method = models.PositiveSmallIntegerField(
|
||||
_("payment method"), choices=PaymentMethod, default=PaymentMethod.SITH_ACCOUNT
|
||||
)
|
||||
is_validated = models.BooleanField(_("is validated"), default=False)
|
||||
|
||||
objects = SellingQuerySet.as_manager()
|
||||
|
||||
@@ -875,10 +872,12 @@ class Selling(models.Model):
|
||||
if not self.date:
|
||||
self.date = timezone.now()
|
||||
self.full_clean()
|
||||
if not self.is_validated:
|
||||
if (
|
||||
self._state.adding
|
||||
and self.payment_method == self.PaymentMethod.SITH_ACCOUNT
|
||||
):
|
||||
self.customer.amount -= self.quantity * self.unit_price
|
||||
self.customer.save(allow_negative=allow_negative)
|
||||
self.is_validated = True
|
||||
user = self.customer.user
|
||||
if user.was_subscribed:
|
||||
if (
|
||||
@@ -948,7 +947,9 @@ class Selling(models.Model):
|
||||
def is_owned_by(self, user: User) -> bool:
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
return self.payment_method != "CARD" and user.is_owner(self.counter)
|
||||
return self.payment_method != self.PaymentMethod.CARD and user.is_owner(
|
||||
self.counter
|
||||
)
|
||||
|
||||
def can_be_viewed_by(self, user: User) -> bool:
|
||||
if (
|
||||
@@ -958,7 +959,7 @@ class Selling(models.Model):
|
||||
return user == self.customer.user
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
if self.payment_method == "SITH_ACCOUNT":
|
||||
if self.payment_method == Selling.PaymentMethod.SITH_ACCOUNT:
|
||||
self.customer.amount += self.quantity * self.unit_price
|
||||
self.customer.save()
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated, Self
|
||||
|
||||
from annotated_types import MinLen
|
||||
from django.urls import reverse
|
||||
from ninja import Field, FilterSchema, ModelSchema, Schema
|
||||
from ninja import FilterLookup, FilterSchema, ModelSchema, Schema
|
||||
from pydantic import model_validator
|
||||
|
||||
from club.schemas import SimpleClubSchema
|
||||
from core.schemas import GroupSchema, SimpleUserSchema
|
||||
from core.schemas import GroupSchema, NonEmptyStr, SimpleUserSchema
|
||||
from counter.models import Counter, Product, ProductType
|
||||
|
||||
|
||||
@@ -21,7 +20,7 @@ class CounterSchema(ModelSchema):
|
||||
|
||||
|
||||
class CounterFilterSchema(FilterSchema):
|
||||
search: Annotated[str, MinLen(1)] = Field(None, q="name__icontains")
|
||||
search: Annotated[NonEmptyStr | None, FilterLookup("name__icontains")] = None
|
||||
|
||||
|
||||
class SimplifiedCounterSchema(ModelSchema):
|
||||
@@ -93,18 +92,18 @@ class ProductSchema(ModelSchema):
|
||||
|
||||
|
||||
class ProductFilterSchema(FilterSchema):
|
||||
search: Annotated[str, MinLen(1)] | None = Field(
|
||||
None, q=["name__icontains", "code__icontains"]
|
||||
)
|
||||
is_archived: bool | None = Field(None, q="archived")
|
||||
buying_groups: set[int] | None = Field(None, q="buying_groups__in")
|
||||
product_type: set[int] | None = Field(None, q="product_type__in")
|
||||
club: set[int] | None = Field(None, q="club__in")
|
||||
counter: set[int] | None = Field(None, q="counters__in")
|
||||
search: Annotated[
|
||||
NonEmptyStr | None, FilterLookup(["name__icontains", "code__icontains"])
|
||||
] = None
|
||||
is_archived: Annotated[bool | None, FilterLookup("archived")] = None
|
||||
buying_groups: Annotated[set[int] | None, FilterLookup("buying_groups__in")] = None
|
||||
product_type: Annotated[set[int] | None, FilterLookup("product_type__in")] = None
|
||||
club: Annotated[set[int] | None, FilterLookup("club__in")] = None
|
||||
counter: Annotated[set[int] | None, FilterLookup("counters__in")] = None
|
||||
|
||||
|
||||
class SaleFilterSchema(FilterSchema):
|
||||
before: datetime | None = Field(None, q="date__lt")
|
||||
after: datetime | None = Field(None, q="date__gt")
|
||||
counters: set[int] | None = Field(None, q="counter__in")
|
||||
products: set[int] | None = Field(None, q="product__in")
|
||||
before: Annotated[datetime | None, FilterLookup("date__lt")] = None
|
||||
after: Annotated[datetime | None, FilterLookup("date__gt")] = None
|
||||
counters: Annotated[set[int] | None, FilterLookup("counter__in")] = None
|
||||
products: Annotated[set[int] | None, FilterLookup("product__in")] = None
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}</td>
|
||||
<td>{{ barman.promo or '' }}</td>
|
||||
<td>{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}</td>
|
||||
<td>{{ barman.perm_sum|format_timedelta }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -73,7 +73,7 @@
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}</td>
|
||||
<td>{{ barman.promo or '' }}</td>
|
||||
<td>{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}</td>
|
||||
<td>{{ barman.perm_sum|format_timedelta }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -116,7 +116,6 @@ class TestAccountDumpCommand(TestAccountDump):
|
||||
operation: Selling = customer.buyings.order_by("date").last()
|
||||
assert operation.unit_price == initial_amount
|
||||
assert operation.counter_id == settings.SITH_COUNTER_ACCOUNT_DUMP_ID
|
||||
assert operation.is_validated is True
|
||||
dump = customer.dumps.last()
|
||||
assert dump.dump_operation == operation
|
||||
|
||||
|
||||
@@ -11,8 +11,12 @@ from model_bakery import baker
|
||||
|
||||
from core.models import Group, User
|
||||
from counter.baker_recipes import counter_recipe, product_recipe
|
||||
from counter.forms import ScheduledProductActionForm, ScheduledProductActionFormSet
|
||||
from counter.models import ScheduledProductAction
|
||||
from counter.forms import (
|
||||
ProductForm,
|
||||
ScheduledProductActionForm,
|
||||
ScheduledProductActionFormSet,
|
||||
)
|
||||
from counter.models import Product, ScheduledProductAction
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -34,6 +38,39 @@ def test_edit_product(client: Client):
|
||||
assert res.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_actions_alongside_product():
|
||||
"""The form should work when the product and the actions are created alongside."""
|
||||
# non-persisted instance
|
||||
product: Product = product_recipe.prepare(_save_related=True)
|
||||
trigger_at = now() + timedelta(minutes=10)
|
||||
form = ProductForm(
|
||||
data={
|
||||
"name": "foo",
|
||||
"description": "bar",
|
||||
"product_type": product.product_type_id,
|
||||
"club": product.club_id,
|
||||
"code": "FOO",
|
||||
"purchase_price": 1.0,
|
||||
"selling_price": 1.0,
|
||||
"special_selling_price": 1.0,
|
||||
"limit_age": 0,
|
||||
"form-TOTAL_FORMS": "2",
|
||||
"form-INITIAL_FORMS": "0",
|
||||
"form-0-task": "counter.tasks.archive_product",
|
||||
"form-0-trigger_at": trigger_at,
|
||||
},
|
||||
)
|
||||
assert form.is_valid()
|
||||
product = form.save()
|
||||
action = ScheduledProductAction.objects.last()
|
||||
assert action.clocked.clocked_time == trigger_at
|
||||
assert action.enabled is True
|
||||
assert action.one_off is True
|
||||
assert action.task == "counter.tasks.archive_product"
|
||||
assert action.kwargs == json.dumps({"product_id": product.id})
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestProductActionForm:
|
||||
def test_single_form_archive(self):
|
||||
|
||||
@@ -53,7 +53,7 @@ def set_age(user: User, age: int):
|
||||
|
||||
|
||||
def force_refill_user(user: User, amount: Decimal | int):
|
||||
baker.make(Refilling, amount=amount, customer=user.customer, is_validated=False)
|
||||
baker.make(Refilling, amount=amount, customer=user.customer)
|
||||
|
||||
|
||||
class TestFullClickBase(TestCase):
|
||||
@@ -115,18 +115,10 @@ class TestRefilling(TestFullClickBase):
|
||||
) -> HttpResponse:
|
||||
used_client = client if client is not None else self.client
|
||||
return used_client.post(
|
||||
reverse(
|
||||
"counter:refilling_create",
|
||||
kwargs={"customer_id": user.pk},
|
||||
),
|
||||
{
|
||||
"amount": str(amount),
|
||||
"payment_method": "CASH",
|
||||
"bank": "OTHER",
|
||||
},
|
||||
reverse("counter:refilling_create", kwargs={"customer_id": user.pk}),
|
||||
{"amount": str(amount), "payment_method": Refilling.PaymentMethod.CASH},
|
||||
HTTP_REFERER=reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": counter.id, "user_id": user.pk},
|
||||
"counter:click", kwargs={"counter_id": counter.id, "user_id": user.pk}
|
||||
),
|
||||
)
|
||||
|
||||
@@ -149,11 +141,7 @@ class TestRefilling(TestFullClickBase):
|
||||
"counter:refilling_create",
|
||||
kwargs={"customer_id": self.customer.pk},
|
||||
),
|
||||
{
|
||||
"amount": "10",
|
||||
"payment_method": "CASH",
|
||||
"bank": "OTHER",
|
||||
},
|
||||
{"amount": "10", "payment_method": "CASH"},
|
||||
)
|
||||
|
||||
self.client.force_login(self.club_admin)
|
||||
|
||||
@@ -298,7 +298,6 @@ def test_update_balance():
|
||||
_quantity=len(customers),
|
||||
unit_price=10,
|
||||
quantity=1,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
_save_related=True,
|
||||
),
|
||||
*sale_recipe.prepare(
|
||||
@@ -306,14 +305,12 @@ def test_update_balance():
|
||||
_quantity=3,
|
||||
unit_price=5,
|
||||
quantity=2,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
_save_related=True,
|
||||
),
|
||||
sale_recipe.prepare(
|
||||
customer=customers[4],
|
||||
quantity=1,
|
||||
unit_price=50,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
_save_related=True,
|
||||
),
|
||||
*sale_recipe.prepare(
|
||||
@@ -324,7 +321,7 @@ def test_update_balance():
|
||||
_quantity=len(customers),
|
||||
unit_price=50,
|
||||
quantity=1,
|
||||
payment_method="CARD",
|
||||
payment_method=Selling.PaymentMethod.CARD,
|
||||
_save_related=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.contrib.auth.models import Permission
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import Client
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import localdate
|
||||
from django.utils.timezone import now
|
||||
from model_bakery import baker
|
||||
from pytest_django.asserts import assertRedirects
|
||||
|
||||
@@ -57,7 +57,7 @@ def test_invoice_call_view(client: Client, query: dict | None):
|
||||
@pytest.mark.django_db
|
||||
def test_invoice_call_form():
|
||||
Selling.objects.all().delete()
|
||||
month = localdate() - relativedelta(months=1)
|
||||
month = now() - relativedelta(months=1)
|
||||
clubs = baker.make(Club, _quantity=2)
|
||||
recipe = sale_recipe.extend(date=month, customer=baker.make(Customer, amount=10000))
|
||||
recipe.make(club=clubs[0], quantity=2, unit_price=200)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
|
||||
@@ -23,6 +23,7 @@ from django.forms.models import modelform_factory
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import get_current_timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import DetailView, ListView, TemplateView
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
@@ -285,7 +286,13 @@ class CounterStatView(PermissionRequiredMixin, DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add stats to the context."""
|
||||
counter: Counter = self.object
|
||||
semester_start = get_start_of_semester()
|
||||
start_date = get_start_of_semester()
|
||||
semester_start = datetime(
|
||||
start_date.year,
|
||||
start_date.month,
|
||||
start_date.day,
|
||||
tzinfo=get_current_timezone(),
|
||||
)
|
||||
office_hours = counter.get_top_barmen()
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs.update(
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
@@ -63,19 +63,18 @@ class InvoiceCallView(
|
||||
"""Add sums to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["months"] = Selling.objects.datetimes("date", "month", order="DESC")
|
||||
start_date = self.get_month()
|
||||
month = self.get_month()
|
||||
start_date = datetime(month.year, month.month, month.day, tzinfo=timezone.utc)
|
||||
end_date = start_date + relativedelta(months=1)
|
||||
|
||||
kwargs["sum_cb"] = Refilling.objects.filter(
|
||||
payment_method="CARD",
|
||||
is_validated=True,
|
||||
payment_method=Refilling.PaymentMethod.CARD,
|
||||
date__gte=start_date,
|
||||
date__lte=end_date,
|
||||
).aggregate(res=Sum("amount", default=0))["res"]
|
||||
kwargs["sum_cb"] += (
|
||||
Selling.objects.filter(
|
||||
payment_method="CARD",
|
||||
is_validated=True,
|
||||
payment_method=Selling.PaymentMethod.CARD,
|
||||
date__gte=start_date,
|
||||
date__lte=end_date,
|
||||
)
|
||||
|
||||
@@ -110,7 +110,9 @@ class Basket(models.Model):
|
||||
)["total"]
|
||||
)
|
||||
|
||||
def generate_sales(self, counter, seller: User, payment_method: str):
|
||||
def generate_sales(
|
||||
self, counter, seller: User, payment_method: Selling.PaymentMethod
|
||||
):
|
||||
"""Generate a list of sold items corresponding to the items
|
||||
of this basket WITHOUT saving them NOR deleting the basket.
|
||||
|
||||
@@ -251,8 +253,7 @@ class Invoice(models.Model):
|
||||
customer=customer,
|
||||
operator=self.user,
|
||||
amount=i.product_unit_price * i.quantity,
|
||||
payment_method="CARD",
|
||||
bank="OTHER",
|
||||
payment_method=Refilling.PaymentMethod.CARD,
|
||||
date=self.date,
|
||||
)
|
||||
new.save()
|
||||
@@ -267,8 +268,7 @@ class Invoice(models.Model):
|
||||
customer=customer,
|
||||
unit_price=i.product_unit_price,
|
||||
quantity=i.quantity,
|
||||
payment_method="CARD",
|
||||
is_validated=True,
|
||||
payment_method=Selling.PaymentMethod.CARD,
|
||||
date=self.date,
|
||||
)
|
||||
new.save()
|
||||
|
||||
@@ -108,12 +108,22 @@ def test_eboutic_basket_expiry(
|
||||
|
||||
client.force_login(customer.user)
|
||||
|
||||
for date in sellings:
|
||||
if sellings:
|
||||
sale_recipe.make(
|
||||
customer=customer, counter=eboutic, date=date, is_validated=True
|
||||
customer=customer,
|
||||
counter=eboutic,
|
||||
date=iter(sellings),
|
||||
_quantity=len(sellings),
|
||||
_bulk_create=True,
|
||||
)
|
||||
if refillings:
|
||||
refill_recipe.make(
|
||||
customer=customer,
|
||||
counter=eboutic,
|
||||
date=iter(refillings),
|
||||
_quantity=len(refillings),
|
||||
_bulk_create=True,
|
||||
)
|
||||
for date in refillings:
|
||||
refill_recipe.make(customer=customer, counter=eboutic, date=date)
|
||||
|
||||
assert (
|
||||
f'x-data="basket({int(expected.timestamp() * 1000) if expected else "null"})"'
|
||||
|
||||
@@ -114,13 +114,13 @@ class TestPaymentSith(TestPaymentBase):
|
||||
"quantity"
|
||||
)
|
||||
assert len(sellings) == 2
|
||||
assert sellings[0].payment_method == "SITH_ACCOUNT"
|
||||
assert sellings[0].payment_method == Selling.PaymentMethod.SITH_ACCOUNT
|
||||
assert sellings[0].quantity == 1
|
||||
assert sellings[0].unit_price == self.snack.selling_price
|
||||
assert sellings[0].counter.type == "EBOUTIC"
|
||||
assert sellings[0].product == self.snack
|
||||
|
||||
assert sellings[1].payment_method == "SITH_ACCOUNT"
|
||||
assert sellings[1].payment_method == Selling.PaymentMethod.SITH_ACCOUNT
|
||||
assert sellings[1].quantity == 2
|
||||
assert sellings[1].unit_price == self.beer.selling_price
|
||||
assert sellings[1].counter.type == "EBOUTIC"
|
||||
@@ -198,13 +198,13 @@ class TestPaymentCard(TestPaymentBase):
|
||||
"quantity"
|
||||
)
|
||||
assert len(sellings) == 2
|
||||
assert sellings[0].payment_method == "CARD"
|
||||
assert sellings[0].payment_method == Selling.PaymentMethod.CARD
|
||||
assert sellings[0].quantity == 1
|
||||
assert sellings[0].unit_price == self.snack.selling_price
|
||||
assert sellings[0].counter.type == "EBOUTIC"
|
||||
assert sellings[0].product == self.snack
|
||||
|
||||
assert sellings[1].payment_method == "CARD"
|
||||
assert sellings[1].payment_method == Selling.PaymentMethod.CARD
|
||||
assert sellings[1].quantity == 2
|
||||
assert sellings[1].unit_price == self.beer.selling_price
|
||||
assert sellings[1].counter.type == "EBOUTIC"
|
||||
|
||||
@@ -275,7 +275,9 @@ class EbouticPayWithSith(CanViewMixin, SingleObjectMixin, View):
|
||||
return redirect("eboutic:payment_result", "failure")
|
||||
|
||||
eboutic = get_eboutic()
|
||||
sales = basket.generate_sales(eboutic, basket.user, "SITH_ACCOUNT")
|
||||
sales = basket.generate_sales(
|
||||
eboutic, basket.user, Selling.PaymentMethod.SITH_ACCOUNT
|
||||
)
|
||||
try:
|
||||
with transaction.atomic():
|
||||
# Selling.save has some important business logic in it.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-11-12 21:44+0100\n"
|
||||
"POT-Creation-Date: 2025-11-24 11:05+0100\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@@ -2658,8 +2658,8 @@ msgid "Buyings"
|
||||
msgstr "Achats"
|
||||
|
||||
#: core/templates/core/user_stats.jinja
|
||||
msgid "Product top 10"
|
||||
msgstr "Top 10 produits"
|
||||
msgid "Product top 15"
|
||||
msgstr "Top 15 produits"
|
||||
|
||||
#: core/templates/core/user_stats.jinja
|
||||
msgid "Product"
|
||||
@@ -2819,8 +2819,8 @@ msgstr "Outils Trombi"
|
||||
#, python-format
|
||||
msgid "%(nb_days)d day, %(remainder)s"
|
||||
msgid_plural "%(nb_days)d days, %(remainder)s"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "%(nb_days)d jour, %(remainder)s"
|
||||
msgstr[1] "%(nb_days)d jours, %(remainder)s"
|
||||
|
||||
#: core/views/files.py
|
||||
msgid "Add a new folder"
|
||||
@@ -2928,18 +2928,6 @@ msgstr "Photos"
|
||||
msgid "Account"
|
||||
msgstr "Compte"
|
||||
|
||||
#: counter/apps.py sith/settings.py
|
||||
msgid "Check"
|
||||
msgstr "Chèque"
|
||||
|
||||
#: counter/apps.py sith/settings.py
|
||||
msgid "Cash"
|
||||
msgstr "Espèces"
|
||||
|
||||
#: counter/apps.py counter/models.py sith/settings.py
|
||||
msgid "Credit card"
|
||||
msgstr "Carte bancaire"
|
||||
|
||||
#: counter/apps.py counter/models.py
|
||||
msgid "counter"
|
||||
msgstr "comptoir"
|
||||
@@ -3152,22 +3140,30 @@ msgstr "vendeurs"
|
||||
msgid "token"
|
||||
msgstr "jeton"
|
||||
|
||||
#: counter/models.py sith/settings.py
|
||||
msgid "Credit card"
|
||||
msgstr "Carte bancaire"
|
||||
|
||||
#: counter/models.py sith/settings.py
|
||||
msgid "Cash"
|
||||
msgstr "Espèces"
|
||||
|
||||
#: counter/models.py sith/settings.py
|
||||
msgid "Check"
|
||||
msgstr "Chèque"
|
||||
|
||||
#: counter/models.py subscription/models.py
|
||||
msgid "payment method"
|
||||
msgstr "méthode de paiement"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "bank"
|
||||
msgstr "banque"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "is validated"
|
||||
msgstr "est validé"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "refilling"
|
||||
msgstr "rechargement"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "Sith account"
|
||||
msgstr "Compte utilisateur"
|
||||
|
||||
#: counter/models.py eboutic/models.py
|
||||
msgid "unit price"
|
||||
msgstr "prix unitaire"
|
||||
@@ -3176,10 +3172,6 @@ msgstr "prix unitaire"
|
||||
msgid "quantity"
|
||||
msgstr "quantité"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "Sith account"
|
||||
msgstr "Compte utilisateur"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "selling"
|
||||
msgstr "vente"
|
||||
@@ -3332,6 +3324,10 @@ msgid ""
|
||||
"“%(value)s” value has the correct format (YYYY-MM) but it is an invalid date."
|
||||
msgstr "La valeur « %(value)s » a le bon format, mais est une date invalide."
|
||||
|
||||
#: counter/models.py
|
||||
msgid "is validated"
|
||||
msgstr "est validé"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "invoice date"
|
||||
msgstr "date de la facture"
|
||||
|
||||
@@ -24,6 +24,7 @@ from ast import literal_eval
|
||||
from enum import Enum
|
||||
|
||||
from django import forms
|
||||
from django.db.models import F
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -34,7 +35,7 @@ from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
||||
|
||||
from core.auth.mixins import FormerSubscriberMixin
|
||||
from core.models import User
|
||||
from core.views import search_user
|
||||
from core.schemas import UserFilterSchema
|
||||
from core.views.forms import SelectDate
|
||||
|
||||
# Enum to select search type
|
||||
@@ -126,11 +127,13 @@ class SearchFormListView(FormerSubscriberMixin, SingleObjectMixin, ListView):
|
||||
q = q.filter(phone=self.valid_form["phone"]).all()
|
||||
elif self.search_type == SearchType.QUICK:
|
||||
if self.valid_form["quick"].strip():
|
||||
q = search_user(self.valid_form["quick"])
|
||||
q = list(
|
||||
UserFilterSchema(search=self.valid_form["quick"])
|
||||
.filter(User.objects.viewable_by(self.request.user))
|
||||
.order_by(F("last_login").desc(nulls_last=True))
|
||||
)
|
||||
else:
|
||||
q = []
|
||||
if not self.can_see_hidden and len(q) > 0:
|
||||
q = [user for user in q if user.is_viewable]
|
||||
else:
|
||||
search_dict = {}
|
||||
for key, value in self.valid_form.items():
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import Literal
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from django.db.models import Q
|
||||
from django.utils import html
|
||||
from haystack.query import SearchQuerySet
|
||||
from ninja import FilterSchema, ModelSchema, Schema
|
||||
from ninja import FilterLookup, FilterSchema, ModelSchema, Schema
|
||||
from pydantic import AliasPath, ConfigDict, Field, TypeAdapter
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
@@ -114,13 +114,14 @@ class UvSchema(ModelSchema):
|
||||
|
||||
|
||||
class UvFilterSchema(FilterSchema):
|
||||
search: str | None = Field(None, q="code__icontains")
|
||||
search: Annotated[str | None, FilterLookup("code__icontains")] = None
|
||||
semester: set[Literal["AUTUMN", "SPRING"]] | None = None
|
||||
credit_type: set[Literal["CS", "TM", "EC", "OM", "QC"]] | None = Field(
|
||||
None, q="credit_type__in"
|
||||
)
|
||||
credit_type: Annotated[
|
||||
set[Literal["CS", "TM", "EC", "OM", "QC"]] | None,
|
||||
FilterLookup("credit_type__in"),
|
||||
] = None
|
||||
language: str = "FR"
|
||||
department: set[str] | None = Field(None, q="department__in")
|
||||
department: Annotated[set[str] | None, FilterLookup("department__in")] = None
|
||||
|
||||
def filter_search(self, value: str | None) -> Q:
|
||||
"""Special filter for the search text.
|
||||
|
||||
@@ -19,37 +19,37 @@ authors = [
|
||||
license = { text = "GPL-3.0-only" }
|
||||
requires-python = "<4.0,>=3.12"
|
||||
dependencies = [
|
||||
"django>=5.2.9,<6.0.0",
|
||||
"django-ninja>=1.5.0,<2.0.0",
|
||||
"django-ninja-extra>=0.30.6,<1.0.0",
|
||||
"django>=5.2.8,<6.0.0",
|
||||
"django-ninja>=1.5.0,<6.0.0",
|
||||
"django-ninja-extra>=0.30.6",
|
||||
"Pillow>=12.0.0,<13.0.0",
|
||||
"mistune>=3.1.4,<4.0.0",
|
||||
"django-jinja<3.0.0,>=2.11.0",
|
||||
"cryptography>=46.0.3,<47.0.0",
|
||||
"django-phonenumber-field>=8.4.0,<9.0.0",
|
||||
"phonenumbers>=9.0.20,<10.0.0",
|
||||
"reportlab>=4.4.6,<5.0.0",
|
||||
"django-phonenumber-field>=8.3.0,<9.0.0",
|
||||
"phonenumbers>=9.0.18,<10.0.0",
|
||||
"reportlab>=4.4.4,<5.0.0",
|
||||
"django-haystack<4.0.0,>=3.3.0",
|
||||
"xapian-haystack<4.0.0,>=3.1.0",
|
||||
"libsass<1.0.0,>=0.23.0",
|
||||
"django-ordered-model<4.0.0,>=3.7.4",
|
||||
"django-simple-captcha<1.0.0,>=0.6.3",
|
||||
"django-simple-captcha<1.0.0,>=0.6.2",
|
||||
"python-dateutil<3.0.0.0,>=2.9.0.post0",
|
||||
"sentry-sdk>=2.47.0,<3.0.0",
|
||||
"sentry-sdk>=2.43.0,<3.0.0",
|
||||
"jinja2<4.0.0,>=3.1.6",
|
||||
"django-countries>=8.2.0,<9.0.0",
|
||||
"django-countries>=8.0.0,<9.0.0",
|
||||
"dict2xml>=1.7.7,<2.0.0",
|
||||
"Sphinx<6,>=5",
|
||||
"tomli>=2.3.0,<3.0.0",
|
||||
"django-honeypot>=1.3.0,<2",
|
||||
"pydantic-extra-types>=2.10.6,<3.0.0",
|
||||
"ical>=11.1.0,<12",
|
||||
"redis[hiredis]<7,>=6.4.0",
|
||||
"redis[hiredis]<7,>=5.3.0",
|
||||
"environs[django]>=14.5.0,<15.0.0",
|
||||
"requests>=2.32.5,<3.0.0",
|
||||
"honcho>=2.0.0",
|
||||
"psutil>=7.1.3,<8.0.0",
|
||||
"celery[redis]>=5.6.0",
|
||||
"celery[redis]>=5.5.2",
|
||||
"django-celery-results>=2.5.1",
|
||||
"django-celery-beat>=2.7.0",
|
||||
]
|
||||
@@ -60,13 +60,13 @@ documentation = "https://sith-ae.readthedocs.io/"
|
||||
|
||||
[dependency-groups]
|
||||
prod = [
|
||||
"psycopg[c]>=3.3.2,<4.0.0",
|
||||
"psycopg[c]>=3.2.12,<4.0.0",
|
||||
]
|
||||
dev = [
|
||||
"django-debug-toolbar>=6.1.0,<7",
|
||||
"ipython>=9.8.0,<10.0.0",
|
||||
"pre-commit>=4.5.0,<5.0.0",
|
||||
"ruff>=0.14.9,<1.0.0",
|
||||
"ipython>=9.7.0,<10.0.0",
|
||||
"pre-commit>=4.3.0,<5.0.0",
|
||||
"ruff>=0.14.4,<1.0.0",
|
||||
"djhtml>=3.0.10,<4.0.0",
|
||||
"faker>=37.12.0,<38.0.0",
|
||||
"rjsmin>=1.2.5,<2.0.0",
|
||||
@@ -77,14 +77,14 @@ tests = [
|
||||
"pytest-cov>=7.0.0,<8.0.0",
|
||||
"pytest-django<5.0.0,>=4.10.0",
|
||||
"model-bakery<2.0.0,>=1.20.4",
|
||||
"beautifulsoup4>=4.14.3,<5",
|
||||
"beautifulsoup4>=4.14.2,<5",
|
||||
"lxml>=6.0.2,<7",
|
||||
]
|
||||
docs = [
|
||||
"mkdocs<2.0.0,>=1.6.1",
|
||||
"mkdocs-material>=9.7.0,<10.0.0",
|
||||
"mkdocstrings>=0.30.1,<1.0.0",
|
||||
"mkdocstrings-python>=1.19.0,<2.0.0",
|
||||
"mkdocs-material>=9.6.23,<10.0.0",
|
||||
"mkdocstrings>=0.30.1,<2.0.0",
|
||||
"mkdocstrings-python>=1.18.2,<2.0.0",
|
||||
"mkdocs-include-markdown-plugin>=7.2.0,<8.0.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ class TestMergeUser(TestCase):
|
||||
seller=self.root,
|
||||
unit_price=2,
|
||||
quantity=2,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
).save()
|
||||
Selling(
|
||||
label="barbar",
|
||||
@@ -125,7 +124,6 @@ class TestMergeUser(TestCase):
|
||||
seller=self.root,
|
||||
unit_price=2,
|
||||
quantity=4,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
).save()
|
||||
today = localtime(now()).date()
|
||||
# both subscriptions began last month and shall end in 5 months
|
||||
@@ -197,7 +195,6 @@ class TestMergeUser(TestCase):
|
||||
seller=self.root,
|
||||
unit_price=2,
|
||||
quantity=4,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
).save()
|
||||
data = {"user1": self.to_keep.id, "user2": self.to_delete.id}
|
||||
res = self.client.post(reverse("rootplace:merge"), data)
|
||||
@@ -225,7 +222,6 @@ class TestMergeUser(TestCase):
|
||||
seller=self.root,
|
||||
unit_price=2,
|
||||
quantity=4,
|
||||
payment_method="SITH_ACCOUNT",
|
||||
).save()
|
||||
data = {"user1": self.to_keep.id, "user2": self.to_delete.id}
|
||||
res = self.client.post(reverse("rootplace:merge"), data)
|
||||
|
||||
@@ -2,20 +2,19 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
|
||||
from annotated_types import MinLen
|
||||
from django.urls import reverse
|
||||
from ninja import FilterSchema, ModelSchema, Schema
|
||||
from ninja import FilterLookup, FilterSchema, ModelSchema, Schema
|
||||
from pydantic import Field, NonNegativeInt
|
||||
|
||||
from core.schemas import SimpleUserSchema, UserProfileSchema
|
||||
from core.schemas import NonEmptyStr, SimpleUserSchema, UserProfileSchema
|
||||
from sas.models import Album, Picture, PictureModerationRequest
|
||||
|
||||
|
||||
class AlbumFilterSchema(FilterSchema):
|
||||
search: Annotated[str, MinLen(1)] | None = Field(None, q="name__icontains")
|
||||
before_date: datetime | None = Field(None, q="event_date__lte")
|
||||
after_date: datetime | None = Field(None, q="event_date__gte")
|
||||
parent_id: int | None = Field(None, q="parent_id")
|
||||
search: Annotated[NonEmptyStr | None, FilterLookup("name__icontains")] = None
|
||||
before_date: Annotated[datetime | None, FilterLookup("event_date__lte")] = None
|
||||
after_date: Annotated[datetime | None, FilterLookup("event_date__gte")] = None
|
||||
parent_id: Annotated[int | None, FilterLookup("parent_id")] = None
|
||||
|
||||
|
||||
class SimpleAlbumSchema(ModelSchema):
|
||||
@@ -60,10 +59,12 @@ class AlbumAutocompleteSchema(ModelSchema):
|
||||
|
||||
|
||||
class PictureFilterSchema(FilterSchema):
|
||||
before_date: datetime | None = Field(None, q="date__lte")
|
||||
after_date: datetime | None = Field(None, q="date__gte")
|
||||
users_identified: set[int] | None = Field(None, q="people__user_id__in")
|
||||
album_id: int | None = Field(None, q="parent_id")
|
||||
before_date: Annotated[datetime | None, FilterLookup("date__lte")] = None
|
||||
after_date: Annotated[datetime | None, FilterLookup("date__gte")] = None
|
||||
users_identified: Annotated[
|
||||
set[int] | None, FilterLookup("people__user_id__in")
|
||||
] = None
|
||||
album_id: Annotated[int | None, FilterLookup("parent_id")] = None
|
||||
|
||||
|
||||
class PictureSchema(ModelSchema):
|
||||
|
||||
@@ -177,7 +177,6 @@ TEMPLATES = [
|
||||
"filters": {
|
||||
"markdown": "core.templatetags.renderer.markdown",
|
||||
"phonenumber": "core.templatetags.renderer.phonenumber",
|
||||
"truncate_time": "core.templatetags.renderer.truncate_time",
|
||||
"format_timedelta": "core.templatetags.renderer.format_timedelta",
|
||||
"add_attr": "core.templatetags.renderer.add_attr",
|
||||
},
|
||||
@@ -216,7 +215,7 @@ TEMPLATES = [
|
||||
},
|
||||
},
|
||||
]
|
||||
FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
|
||||
|
||||
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
"default": {
|
||||
@@ -440,19 +439,6 @@ SITH_SUBSCRIPTION_LOCATIONS = [
|
||||
|
||||
SITH_COUNTER_BARS = [(1, "MDE"), (2, "Foyer"), (35, "La Gommette")]
|
||||
|
||||
SITH_COUNTER_BANK = [
|
||||
("OTHER", "Autre"),
|
||||
("SOCIETE-GENERALE", "Société générale"),
|
||||
("BANQUE-POPULAIRE", "Banque populaire"),
|
||||
("BNP", "BNP"),
|
||||
("CAISSE-EPARGNE", "Caisse d'épargne"),
|
||||
("CIC", "CIC"),
|
||||
("CREDIT-AGRICOLE", "Crédit Agricole"),
|
||||
("CREDIT-MUTUEL", "Credit Mutuel"),
|
||||
("CREDIT-LYONNAIS", "Credit Lyonnais"),
|
||||
("LA-POSTE", "La Poste"),
|
||||
]
|
||||
|
||||
SITH_PEDAGOGY_UV_TYPE = [
|
||||
("FREE", _("Free")),
|
||||
("CS", _("CS")),
|
||||
|
||||
@@ -24,7 +24,6 @@ from django.views.generic import CreateView, DetailView, TemplateView
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
from core.views.group import PermissionGroupsUpdateView
|
||||
from counter.apps import PAYMENT_METHOD
|
||||
from subscription.forms import (
|
||||
SelectionDateForm,
|
||||
SubscriptionExistingUserForm,
|
||||
@@ -129,6 +128,6 @@ class SubscriptionsStatsView(FormView):
|
||||
subscription_end__gte=self.end_date, subscription_start__lte=self.start_date
|
||||
)
|
||||
kwargs["subscriptions_types"] = settings.SITH_SUBSCRIPTIONS
|
||||
kwargs["payment_types"] = PAYMENT_METHOD
|
||||
kwargs["payment_types"] = settings.SITH_SUBSCRIPTION_PAYMENT_METHOD
|
||||
kwargs["locations"] = settings.SITH_SUBSCRIPTION_LOCATIONS
|
||||
return kwargs
|
||||
|
||||
367
uv.lock
generated
367
uv.lock
generated
@@ -75,24 +75,24 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.14.3"
|
||||
version = "4.14.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "soupsieve" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "billiard"
|
||||
version = "4.2.4"
|
||||
version = "4.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/23/b12ac0bcdfb7360d664f40a00b1bda139cbbbced012c34e375506dbd0143/billiard-4.2.4.tar.gz", hash = "sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f", size = 156537, upload-time = "2025-11-30T13:28:48.52Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/50/cc2b8b6e6433918a6b9a3566483b743dcd229da1e974be9b5f259db3aad7/billiard-4.2.3.tar.gz", hash = "sha256:96486f0885afc38219d02d5f0ccd5bec8226a414b834ab244008cbb0025b8dcb", size = 156450, upload-time = "2025-11-16T17:47:30.281Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/87/8bab77b323f16d67be364031220069f79159117dd5e43eeb4be2fef1ac9b/billiard-4.2.4-py3-none-any.whl", hash = "sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5", size = 87070, upload-time = "2025-11-30T13:28:47.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/cc/38b6f87170908bd8aaf9e412b021d17e85f690abe00edf50192f1a4566b9/billiard-4.2.3-py3-none-any.whl", hash = "sha256:989e9b688e3abf153f307b68a1328dfacfb954e30a4f920005654e276c69236b", size = 87042, upload-time = "2025-11-16T17:47:29.005Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -106,7 +106,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "celery"
|
||||
version = "5.6.0"
|
||||
version = "5.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "billiard" },
|
||||
@@ -114,20 +114,18 @@ dependencies = [
|
||||
{ name = "click-didyoumean" },
|
||||
{ name = "click-plugins" },
|
||||
{ name = "click-repl" },
|
||||
{ name = "exceptiongroup" },
|
||||
{ name = "kombu" },
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "tzlocal" },
|
||||
{ name = "vine" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ad/5f/b681ae3c89290d2ea6562ea96b40f5af6f6fc5f7743e2cd1a19e47721548/celery-5.6.0.tar.gz", hash = "sha256:641405206042d52ae460e4e9751a2e31b06cf80ab836fcf92e0b9311d7ea8113", size = 1712522, upload-time = "2025-11-30T17:39:46.282Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/03/5d9c6c449248958f1a5870e633a29d7419ff3724c452a98ffd22688a1a6a/celery-5.5.2.tar.gz", hash = "sha256:4d6930f354f9d29295425d7a37261245c74a32807c45d764bedc286afd0e724e", size = 1666892, upload-time = "2025-04-25T20:10:04.695Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/4e/53a125038d6a814491a0ae3457435c13cf8821eb602292cf9db37ce35f62/celery-5.6.0-py3-none-any.whl", hash = "sha256:33cf01477b175017fc8f22c5ee8a65157591043ba8ca78a443fe703aa910f581", size = 444561, upload-time = "2025-11-30T17:39:44.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/94/8e825ac1cf59d45d20c4345d4461e6b5263ae475f708d047c3dad0ac6401/celery-5.5.2-py3-none-any.whl", hash = "sha256:54425a067afdc88b57cd8d94ed4af2ffaf13ab8c7680041ac2c4ac44357bdf4c", size = 438626, upload-time = "2025-04-25T20:10:01.383Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
redis = [
|
||||
{ name = "kombu", extra = ["redis"] },
|
||||
{ name = "redis" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -331,76 +329,76 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.13.0"
|
||||
version = "7.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/f1/2619559f17f31ba00fc40908efd1fbf1d0a5536eb75dc8341e7d660a08de/coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf", size = 218274, upload-time = "2025-12-08T13:12:52.095Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/11/30d71ae5d6e949ff93b2a79a2c1b4822e00423116c5c6edfaeef37301396/coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f", size = 218638, upload-time = "2025-12-08T13:12:53.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/c2/fce80fc6ded8d77e53207489d6065d0fed75db8951457f9213776615e0f5/coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb", size = 250129, upload-time = "2025-12-08T13:12:54.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621", size = 252885, upload-time = "2025-12-08T13:12:56.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/f8/972a5affea41de798691ab15d023d3530f9f56a72e12e243f35031846ff7/coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74", size = 253974, upload-time = "2025-12-08T13:12:57.718Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/56/116513aee860b2c7968aa3506b0f59b22a959261d1dbf3aea7b4450a7520/coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57", size = 250538, upload-time = "2025-12-08T13:12:59.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/75/074476d64248fbadf16dfafbf93fdcede389ec821f74ca858d7c87d2a98c/coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8", size = 251912, upload-time = "2025-12-08T13:13:00.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/d2/aa4f8acd1f7c06024705c12609d8698c51b27e4d635d717cd1934c9668e2/coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d", size = 250054, upload-time = "2025-12-08T13:13:01.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/98/8df9e1af6a493b03694a1e8070e024e7d2cdc77adedc225a35e616d505de/coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b", size = 249619, upload-time = "2025-12-08T13:13:03.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/71/f8679231f3353018ca66ef647fa6fe7b77e6bff7845be54ab84f86233363/coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd", size = 251496, upload-time = "2025-12-08T13:13:04.511Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/86/9cb406388034eaf3c606c22094edbbb82eea1fa9d20c0e9efadff20d0733/coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef", size = 220808, upload-time = "2025-12-08T13:13:06.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/59/af483673df6455795daf5f447c2f81a3d2fcfc893a22b8ace983791f6f34/coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae", size = 221616, upload-time = "2025-12-08T13:13:07.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/b0/959d582572b30a6830398c60dd419c1965ca4b5fb38ac6b7093a0d50ca8d/coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080", size = 220261, upload-time = "2025-12-08T13:13:09.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/bf/638c0427c0f0d47638242e2438127f3c8ee3cfc06c7fdeb16778ed47f836/coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647", size = 217704, upload-time = "2025-11-18T13:32:28.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/e1/706fae6692a66c2d6b871a608bbde0da6281903fa0e9f53a39ed441da36a/coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736", size = 218064, upload-time = "2025-11-18T13:32:30.161Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/8b/eb0231d0540f8af3ffda39720ff43cb91926489d01524e68f60e961366e4/coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60", size = 249560, upload-time = "2025-11-18T13:32:31.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/a1/67fb52af642e974d159b5b379e4d4c59d0ebe1288677fbd04bbffe665a82/coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8", size = 252318, upload-time = "2025-11-18T13:32:33.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/e5/38228f31b2c7665ebf9bdfdddd7a184d56450755c7e43ac721c11a4b8dab/coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f", size = 253403, upload-time = "2025-11-18T13:32:34.45Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/4b/df78e4c8188f9960684267c5a4897836f3f0f20a20c51606ee778a1d9749/coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70", size = 249984, upload-time = "2025-11-18T13:32:35.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/51/bb163933d195a345c6f63eab9e55743413d064c291b6220df754075c2769/coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0", size = 251339, upload-time = "2025-11-18T13:32:37.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/40/c9b29cdb8412c837cdcbc2cfa054547dd83affe6cbbd4ce4fdb92b6ba7d1/coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068", size = 249489, upload-time = "2025-11-18T13:32:39.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/da/b3131e20ba07a0de4437a50ef3b47840dfabf9293675b0cd5c2c7f66dd61/coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b", size = 249070, upload-time = "2025-11-18T13:32:40.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/81/b653329b5f6302c08d683ceff6785bc60a34be9ae92a5c7b63ee7ee7acec/coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937", size = 250929, upload-time = "2025-11-18T13:32:42.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/00/250ac3bca9f252a5fb1338b5ad01331ebb7b40223f72bef5b1b2cb03aa64/coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa", size = 220241, upload-time = "2025-11-18T13:32:44.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/1c/77e79e76d37ce83302f6c21980b45e09f8aa4551965213a10e62d71ce0ab/coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a", size = 221051, upload-time = "2025-11-18T13:32:46.008Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/f5/641b8a25baae564f9e52cac0e2667b123de961985709a004e287ee7663cc/coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c", size = 219692, upload-time = "2025-11-18T13:32:47.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941", size = 217725, upload-time = "2025-11-18T13:32:49.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a", size = 218098, upload-time = "2025-11-18T13:32:50.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d", size = 249093, upload-time = "2025-11-18T13:32:52.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211", size = 251686, upload-time = "2025-11-18T13:32:54.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d", size = 252930, upload-time = "2025-11-18T13:32:56.417Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c", size = 249296, upload-time = "2025-11-18T13:32:58.074Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9", size = 251068, upload-time = "2025-11-18T13:32:59.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0", size = 249034, upload-time = "2025-11-18T13:33:01.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508", size = 248853, upload-time = "2025-11-18T13:33:02.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc", size = 250619, upload-time = "2025-11-18T13:33:04.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8", size = 220261, upload-time = "2025-11-18T13:33:06.493Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07", size = 221072, upload-time = "2025-11-18T13:33:07.926Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc", size = 219702, upload-time = "2025-11-18T13:33:09.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87", size = 218420, upload-time = "2025-11-18T13:33:11.153Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6", size = 218773, upload-time = "2025-11-18T13:33:12.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7", size = 260078, upload-time = "2025-11-18T13:33:14.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560", size = 262144, upload-time = "2025-11-18T13:33:15.601Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12", size = 264574, upload-time = "2025-11-18T13:33:17.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296", size = 259298, upload-time = "2025-11-18T13:33:18.958Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507", size = 262150, upload-time = "2025-11-18T13:33:20.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d", size = 259763, upload-time = "2025-11-18T13:33:22.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2", size = 258653, upload-time = "2025-11-18T13:33:24.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455", size = 260856, upload-time = "2025-11-18T13:33:26.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d", size = 220936, upload-time = "2025-11-18T13:33:28.165Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c", size = 222001, upload-time = "2025-11-18T13:33:29.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d", size = 220273, upload-time = "2025-11-18T13:33:31.415Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92", size = 217777, upload-time = "2025-11-18T13:33:32.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360", size = 218100, upload-time = "2025-11-18T13:33:34.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac", size = 249151, upload-time = "2025-11-18T13:33:36.135Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d", size = 251667, upload-time = "2025-11-18T13:33:37.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c", size = 253003, upload-time = "2025-11-18T13:33:39.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434", size = 249185, upload-time = "2025-11-18T13:33:42.086Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc", size = 251025, upload-time = "2025-11-18T13:33:43.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc", size = 248979, upload-time = "2025-11-18T13:33:46.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e", size = 248800, upload-time = "2025-11-18T13:33:47.546Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17", size = 250460, upload-time = "2025-11-18T13:33:49.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933", size = 220533, upload-time = "2025-11-18T13:33:51.16Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe", size = 221348, upload-time = "2025-11-18T13:33:52.776Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d", size = 219922, upload-time = "2025-11-18T13:33:54.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d", size = 218511, upload-time = "2025-11-18T13:33:56.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03", size = 218771, upload-time = "2025-11-18T13:33:57.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9", size = 260151, upload-time = "2025-11-18T13:33:59.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6", size = 262257, upload-time = "2025-11-18T13:34:01.166Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339", size = 264671, upload-time = "2025-11-18T13:34:02.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e", size = 259231, upload-time = "2025-11-18T13:34:04.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13", size = 262137, upload-time = "2025-11-18T13:34:06.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f", size = 259745, upload-time = "2025-11-18T13:34:08.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1", size = 258570, upload-time = "2025-11-18T13:34:09.676Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b", size = 260899, upload-time = "2025-11-18T13:34:11.259Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a", size = 221313, upload-time = "2025-11-18T13:34:12.863Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291", size = 222423, upload-time = "2025-11-18T13:34:15.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384", size = 220459, upload-time = "2025-11-18T13:34:17.222Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -521,16 +519,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.9"
|
||||
version = "5.2.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/1c/188ce85ee380f714b704283013434976df8d3a2df8e735221a02605b6794/django-5.2.9.tar.gz", hash = "sha256:16b5ccfc5e8c27e6c0561af551d2ea32852d7352c67d452ae3e76b4f6b2ca495", size = 10848762, upload-time = "2025-12-02T14:01:08.418Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/a2/933dbbb3dd9990494960f6e64aca2af4c0745b63b7113f59a822df92329e/django-5.2.8.tar.gz", hash = "sha256:23254866a5bb9a2cfa6004e8b809ec6246eba4b58a7589bc2772f1bcc8456c7f", size = 10849032, upload-time = "2025-11-05T14:07:32.778Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/17/b0/7f42bfc38b8f19b78546d47147e083ed06e12fc29c42da95655e0962c6c2/django-5.2.9-py3-none-any.whl", hash = "sha256:3a4ea88a70370557ab1930b332fd2887a9f48654261cdffda663fef5976bb00a", size = 8290652, upload-time = "2025-12-02T14:01:03.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/3d/a035a4ee9b1d4d4beee2ae6e8e12fe6dee5514b21f62504e22efcbd9fb46/django-5.2.8-py3-none-any.whl", hash = "sha256:37e687f7bd73ddf043e2b6b97cfe02fcbb11f2dbb3adccc6a2b18c6daa054d7f", size = 8289692, upload-time = "2025-11-05T14:07:28.761Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -574,15 +572,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-countries"
|
||||
version = "8.2.0"
|
||||
version = "8.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/2e/ed67f8f460d1de25ee64fca5d7f219680f944fc8ac5a29fbede3574dc3db/django_countries-8.2.0.tar.gz", hash = "sha256:6df3883180599052c7dfa9a8be0601792441cfb248935dc229ad1ac92e9e39e3", size = 2455542, upload-time = "2025-11-24T19:57:08.071Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/be/4e/2d776025b81f1da489901738d4db518a80f6835fe417e0142b5065a3f07f/django_countries-8.1.1.tar.gz", hash = "sha256:0d51c2a31a3fe01227df56762f2449e97eb843c53f69cf3c9a3a0d6702e34538", size = 2436829, upload-time = "2025-11-17T20:03:44.993Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/3c/9ebd7ed021b7c519bac954bc88146bc870e7d3c8db2580fa67268464fd2e/django_countries-8.2.0-py3-none-any.whl", hash = "sha256:2b2617bec7c15dc735bdec38ae89f0058e38fddfffdb19a7f6b75ef1e3d5380f", size = 3776079, upload-time = "2025-11-24T19:57:05.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/7b/b4175ab6be6e62063ff0158fe59342ae8a9c61ff5234026490f72430486e/django_countries-8.1.1-py3-none-any.whl", hash = "sha256:7b92907a82d2b44cd4d72d7ad47b2a24c7e7854b6d9dd8f1d2b310f18f659cd6", size = 3759539, upload-time = "2025-11-17T20:03:42.519Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -673,14 +671,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-phonenumber-field"
|
||||
version = "8.4.0"
|
||||
version = "8.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/87/bf/8aa60c9834773b955dff1ddea842e361e8daaf6b0945d5bfc29fc66d53ab/django_phonenumber_field-8.4.0.tar.gz", hash = "sha256:2b83e843dac35eec6a69880a166487235b737a71a1e38c9a52e5ad67d6996083", size = 45512, upload-time = "2025-11-24T15:09:51.904Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cf/65/93e52cdc84803e62be6b2b953ed7b5c7ce21d5ff75e149433c6aaba22c00/django_phonenumber_field-8.3.0.tar.gz", hash = "sha256:193b3acc3b83ff4aad95dd9530a6d01e1c7c1cecbdfa8a56159789bbb85ab0e2", size = 45239, upload-time = "2025-10-06T14:57:01.447Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/a3/f6b85a9246e22cf719752ad83f5e95aa9ba12419b2f9eff70f20d30e55df/django_phonenumber_field-8.4.0-py3-none-any.whl", hash = "sha256:7a1cb3a6456edb54d879f11ffa0acb227ded08c93b587035d0f28093f0e46511", size = 69528, upload-time = "2025-11-24T15:09:45.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/55/fd4a0976805a05402ca04f3938f5d999f3803471a1eef8b65a9e259e5914/django_phonenumber_field-8.3.0-py3-none-any.whl", hash = "sha256:afaf8b16693c8d64646acf7693f5a9c4a809e7f1f9b66a84dabbe04a2d6c1513", size = 69530, upload-time = "2025-10-06T14:57:00.259Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -694,28 +692,28 @@ sdist = { url = "https://files.pythonhosted.org/packages/70/e3/9372fcdca8e9c3205
|
||||
|
||||
[[package]]
|
||||
name = "django-simple-captcha"
|
||||
version = "0.6.3"
|
||||
version = "0.6.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "django-ranged-response" },
|
||||
{ name = "pillow" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/12/dfcfd76d890c23bde772127647f4d61a6138e5e53453d7aa188e5aee0598/django_simple_captcha-0.6.3.tar.gz", hash = "sha256:6d295dac7213946631b88783b420e58d5d67bf2ff1c9704ac57986323e23bbef", size = 125646, upload-time = "2025-12-07T17:45:11.31Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/d5/1dd445fc1a648045044b80be224ca6b2430ffd62dc139e45b2d1b0184cc4/django_simple_captcha-0.6.2.tar.gz", hash = "sha256:24db2bd1386c1833618465862e3518d310246d88f5e7f3bec23a5bda768ef834", size = 123994, upload-time = "2025-02-25T14:43:25.038Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/10/8c/d0d007820174922fbbaae98074d3a6ffc1b1f53f0c0f68302c5f1b4bb67f/django_simple_captcha-0.6.3-py2.py3-none-any.whl", hash = "sha256:822750ee91ac5bb74c86e785b9539ca22cba2c5d83c0bdf7b4eaaf20c0abff84", size = 94099, upload-time = "2025-12-07T17:45:09.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/63/7485d3049c8479f2c77c87aa7557aea300289ad7ac95c9230759f6610c6f/django_simple_captcha-0.6.2-py2.py3-none-any.whl", hash = "sha256:f5b7eee6bfeba6c55b47f2f8414557d5609fc29c4758da2c91ba9a91d6ac349d", size = 93578, upload-time = "2025-02-25T14:43:23.548Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-timezone-field"
|
||||
version = "7.2.1"
|
||||
version = "7.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/da/05/9b93a66452cdb8a08ab26f08d5766d2332673e659a8b2aeb73f2a904d421/django_timezone_field-7.2.1.tar.gz", hash = "sha256:def846f9e7200b7b8f2a28fcce2b78fb2d470f6a9f272b07c4e014f6ba4c6d2e", size = 13096, upload-time = "2025-12-06T23:50:44.591Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/5b/0dbe271fef3c2274b83dbcb1b19fa3dacf1f7e542382819294644e78ea8b/django_timezone_field-7.1.tar.gz", hash = "sha256:b3ef409d88a2718b566fabe10ea996f2838bc72b22d3a2900c0aa905c761380c", size = 13727, upload-time = "2025-01-11T17:49:54.486Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/7f/d885667401515b467f84569c56075bc9add72c9fd425fca51a25f4c997e1/django_timezone_field-7.2.1-py3-none-any.whl", hash = "sha256:276915b72c5816f57c3baf9e43f816c695ef940d1b21f91ebf6203c09bf4ad44", size = 13284, upload-time = "2025-12-06T23:50:43.302Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/09/7a808392a751a24ffa62bec00e3085a9c1a151d728c323a5bab229ea0e58/django_timezone_field-7.1-py3-none-any.whl", hash = "sha256:93914713ed882f5bccda080eda388f7006349f25930b6122e9b07bf8db49c4b4", size = 13177, upload-time = "2025-01-11T17:49:52.142Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -756,18 +754,6 @@ django = [
|
||||
{ name = "django-cache-url" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.2.1"
|
||||
@@ -958,16 +944,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "injector"
|
||||
version = "0.23.0"
|
||||
version = "0.22.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/f5/060088d45fd57db2e2164ae289a96066d3e48320c0c99bba5e50eb245d22/injector-0.23.0.tar.gz", hash = "sha256:7e59db84faa5358474c8ee05abca6ab6bd02519fbefcf175e0b230ee7d2d4bac", size = 55720, upload-time = "2025-12-01T15:16:31.037Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b5/f6/3a74e4b983b92eebb77b2f27ee526aa7eaf0e62a5913847d59361b4bae5f/injector-0.22.0.tar.gz", hash = "sha256:79b2d4a0874c75d3aa735f11c5b32b89d9542711ca07071161882c5e9cc15ed6", size = 86166, upload-time = "2024-07-07T23:02:23.275Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/52/2e771b1acc010f0351e703d524702db72b80ad8b375dccac8488ccc02a61/injector-0.23.0-py2.py3-none-any.whl", hash = "sha256:86ab389805b4aaabfe235b2b2b85ff515af4d0209faccf1d60de2872c92c1fa0", size = 21483, upload-time = "2025-12-01T15:16:29.664Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/37/37fee65c78ae3f9675e6190bfd12304e5d8d99564f0ec91716bf2bfbbb5f/injector-0.22.0-py2.py3-none-any.whl", hash = "sha256:74379ccef3b893bc7d0b7d504c255b5160c5a55e97dc7bdcc73cb33cc7dce3a1", size = 20461, upload-time = "2024-07-07T23:02:21.103Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "9.8.0"
|
||||
version = "9.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
@@ -981,9 +967,9 @@ dependencies = [
|
||||
{ name = "stack-data" },
|
||||
{ name = "traitlets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/12/51/a703c030f4928646d390b4971af4938a1b10c9dfce694f0d99a0bb073cb2/ipython-9.8.0.tar.gz", hash = "sha256:8e4ce129a627eb9dd221c41b1d2cdaed4ef7c9da8c17c63f6f578fe231141f83", size = 4424940, upload-time = "2025-12-03T10:18:24.353Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/29/e6/48c74d54039241a456add616464ea28c6ebf782e4110d419411b83dae06f/ipython-9.7.0.tar.gz", hash = "sha256:5f6de88c905a566c6a9d6c400a8fed54a638e1f7543d17aae2551133216b1e4e", size = 4422115, upload-time = "2025-11-05T12:18:54.646Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/df/8ee1c5dd1e3308b5d5b2f2dfea323bb2f3827da8d654abb6642051199049/ipython-9.8.0-py3-none-any.whl", hash = "sha256:ebe6d1d58d7d988fbf23ff8ff6d8e1622cfdb194daf4b7b73b792c4ec3b85385", size = 621374, upload-time = "2025-12-03T10:18:22.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/aa/62893d6a591d337aa59dcc4c6f6c842f1fe20cd72c8c5c1f980255243252/ipython-9.7.0-py3-none-any.whl", hash = "sha256:bce8ac85eb9521adc94e1845b4c03d88365fd6ac2f4908ec4ed1eb1b0a065f9f", size = 618911, upload-time = "2025-11-05T12:18:52.484Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1024,7 +1010,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "kombu"
|
||||
version = "5.6.1"
|
||||
version = "5.5.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "amqp" },
|
||||
@@ -1032,14 +1018,9 @@ dependencies = [
|
||||
{ name = "tzdata" },
|
||||
{ name = "vine" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/05/749ada8e51718445d915af13f1d18bc4333848e8faa0cb234028a3328ec8/kombu-5.6.1.tar.gz", hash = "sha256:90f1febb57ad4f53ca327a87598191b2520e0c793c75ea3b88d98e3b111282e4", size = 471548, upload-time = "2025-11-25T11:07:33.504Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/d3/5ff936d8319ac86b9c409f1501b07c426e6ad41966fedace9ef1b966e23f/kombu-5.5.4.tar.gz", hash = "sha256:886600168275ebeada93b888e831352fe578168342f0d1d5833d88ba0d847363", size = 461992, upload-time = "2025-06-01T10:19:22.281Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/d6/943cf84117cd9ddecf6e1707a3f712a49fc64abdb8ac31b19132871af1dd/kombu-5.6.1-py3-none-any.whl", hash = "sha256:b69e3f5527ec32fc5196028a36376501682973e9620d6175d1c3d4eaf7e95409", size = 214141, upload-time = "2025-11-25T11:07:31.54Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
redis = [
|
||||
{ name = "redis" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/70/a07dcf4f62598c8ad579df241af55ced65bed76e42e45d3c368a6d82dbc1/kombu-5.5.4-py3-none-any.whl", hash = "sha256:a12ed0557c238897d8e518f1d1fdf84bd1516c5e305af2dacd85c2015115feb8", size = 210034, upload-time = "2025-06-01T10:19:20.436Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1209,11 +1190,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "marshmallow"
|
||||
version = "4.1.1"
|
||||
version = "4.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4f/81/edb105b3296712a282680bc1ae02b8c1bb45d8f1edad3ff9fab1d41e9507/marshmallow-4.1.1.tar.gz", hash = "sha256:550aa14b619072f0a8d8184911b3f1021c5c32587fb27318ddf81ce0d0029c9d", size = 220720, upload-time = "2025-12-05T22:56:09.282Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/2c/e40834adb0bb6f21d7372ad90e616eda82116d4f090d93c29ceb2366cdaf/marshmallow-4.1.0.tar.gz", hash = "sha256:daa9862f74e2f7864980d25c29b4ea72944cde48aa17537e3bd5797a4ae62d71", size = 220619, upload-time = "2025-11-01T15:40:37.096Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/c9/4364652639e3aa952f9e941c38a4f88a0eb29fcbb7bd642fe2de4322c834/marshmallow-4.1.1-py3-none-any.whl", hash = "sha256:9038db4cceb849ce2b8676ccf3d8e5b5e634ac499e291397efa260aa796c385a", size = 48295, upload-time = "2025-12-05T22:56:07.415Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/df/081ea8c41696d598e7cea4f101e49da718a9b6c9dcaaad4e76dfc11a022c/marshmallow-4.1.0-py3-none-any.whl", hash = "sha256:9901660499be3b880dc92d6b5ee0b9a79e94265b7793f71021f92040c07129f1", size = 48286, upload-time = "2025-11-01T15:40:35.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1444,11 +1425,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "phonenumbers"
|
||||
version = "9.0.20"
|
||||
version = "9.0.19"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1b/2a/d6e7fecd1bcd37e65fbb9386e492082bd84e196c9cab14e39166b35cea4b/phonenumbers-9.0.20.tar.gz", hash = "sha256:849788eec8e5a9737a99c8b906d18a62d9fced6497ba7033784b6a7e4c89bb2d", size = 2298276, upload-time = "2025-12-05T12:03:51.072Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/55/5d/ed5a4642c42c16ffbab3d275f516a1db7d14b6a55c1a181c7504c56ed4f7/phonenumbers-9.0.19.tar.gz", hash = "sha256:e0674e31554362f4d95383558f7aefde738ef2e7bf96d28a10afd3e87d63a65c", size = 2298261, upload-time = "2025-11-20T18:37:07.686Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/90/b0/e3fd0ae68bc51593ef6f77a44901d1fa14c1b230cdce848b50ed10881ca3/phonenumbers-9.0.20-py2.py3-none-any.whl", hash = "sha256:03bf5dd14891891284ba96f803d0e5e7e11b9306a0ec4fdf25756ada39eacb86", size = 2584219, upload-time = "2025-12-05T12:03:48.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/3e/151cc6a597d15ae45c932d21e98170e0d5d32b057e495fdb3193725c994a/phonenumbers-9.0.19-py2.py3-none-any.whl", hash = "sha256:004abdfe2010518c2383f148515664a742e8a5d5540e07c049735c139d7e8b09", size = 2584208, upload-time = "2025-11-20T18:37:05.522Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1522,11 +1503,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.5.1"
|
||||
version = "4.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1594,15 +1575,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "psycopg"
|
||||
version = "3.3.2"
|
||||
version = "3.2.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/1a/7d9ef4fdc13ef7f15b934c393edc97a35c281bb7d3c3329fbfcbe915a7c2/psycopg-3.3.2.tar.gz", hash = "sha256:707a67975ee214d200511177a6a80e56e654754c9afca06a7194ea6bbfde9ca7", size = 165630, upload-time = "2025-12-06T17:34:53.899Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/44/05/d4a05988f15fcf90e0088c735b1f2fc04a30b7fc65461d6ec278f5f2f17a/psycopg-3.2.13.tar.gz", hash = "sha256:309adaeda61d44556046ec9a83a93f42bbe5310120b1995f3af49ab6d9f13c1d", size = 160626, upload-time = "2025-11-21T22:34:32.328Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/51/2779ccdf9305981a06b21a6b27e8547c948d85c41c76ff434192784a4c93/psycopg-3.3.2-py3-none-any.whl", hash = "sha256:3e94bc5f4690247d734599af56e51bae8e0db8e4311ea413f801fef82b14a99b", size = 212774, upload-time = "2025-12-06T17:31:41.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/14/f2724bd1986158a348316e86fdd0837a838b14a711df3f00e47fba597447/psycopg-3.2.13-py3-none-any.whl", hash = "sha256:a481374514f2da627157f767a9336705ebefe93ea7a0522a6cbacba165da179a", size = 206797, upload-time = "2025-11-21T22:29:39.733Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -1612,9 +1593,9 @@ c = [
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-c"
|
||||
version = "3.3.2"
|
||||
version = "3.2.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/f5/13c6bf88f6ccadc2930066cc5369cee431fc2c87a1ddb621fc27cfe7d8f3/psycopg_c-3.3.2.tar.gz", hash = "sha256:a65927731d394cc77bbf85d02d0311d7843616a4a627f3e816e94ad3a052ef83", size = 624077, upload-time = "2025-12-06T17:34:55.51Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/d4/f471e7f87c0a75de96a3f17a532412b8866a888383289d54ddb6adb5b54e/psycopg_c-3.2.13.tar.gz", hash = "sha256:3f8d69a563f198198aaf3333e3830c68368a4e45cd60faeabca9949f958f6480", size = 624372, upload-time = "2025-11-21T22:34:34.084Z" }
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
@@ -1645,7 +1626,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.5"
|
||||
version = "2.12.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@@ -1653,9 +1634,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1751,17 +1732,26 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.10.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pymdown-extensions"
|
||||
version = "10.19.1"
|
||||
version = "10.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/2d/9f30cee56d4d6d222430d401e85b0a6a1ae229819362f5786943d1a8c03b/pymdown_extensions-10.19.1.tar.gz", hash = "sha256:4969c691009a389fb1f9712dd8e7bd70dcc418d15a0faf70acb5117d022f7de8", size = 847839, upload-time = "2025-12-14T17:25:24.42Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/a987e4d549c6c82353fce5fa5f650229bb60ea4c0d1684a2714a509aef58/pymdown_extensions-10.17.1.tar.gz", hash = "sha256:60d05fe55e7fb5a1e4740fc575facad20dc6ee3a748e8d3d36ba44142e75ce03", size = 845207, upload-time = "2025-11-11T21:44:58.815Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/35/b763e8fbcd51968329b9adc52d188fc97859f85f2ee15fe9f379987d99c5/pymdown_extensions-10.19.1-py3-none-any.whl", hash = "sha256:e8698a66055b1dc0dca2a7f2c9d0ea6f5faa7834a9c432e3535ab96c0c4e509b", size = 266693, upload-time = "2025-12-14T17:25:22.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/40/b2d7b9fdccc63e48ae4dbd363b6b89eb7ac346ea49ed667bb71f92af3021/pymdown_extensions-10.17.1-py3-none-any.whl", hash = "sha256:1f160209c82eecbb5d8a0d8f89a4d9bd6bdcbde9a8537761844cfc57ad5cd8a6", size = 266310, upload-time = "2025-11-11T21:44:56.809Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1896,11 +1886,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "6.4.0"
|
||||
version = "5.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399, upload-time = "2025-08-07T08:10:11.441Z" }
|
||||
dependencies = [
|
||||
{ name = "pyjwt" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/cf/128b1b6d7086200c9f387bd4be9b2572a30b90745ef078bd8b235042dc9f/redis-5.3.1.tar.gz", hash = "sha256:ca49577a531ea64039b5a36db3d6cd1a0c7a60c34124d46924a45b956e8cf14c", size = 4626200, upload-time = "2025-07-25T08:06:27.778Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847, upload-time = "2025-08-07T08:10:09.84Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/26/5c5fa0e83c3621db835cfc1f1d789b37e7fa99ed54423b5f519beb931aa7/redis-5.3.1-py3-none-any.whl", hash = "sha256:dc1909bd24669cc31b5f67a039700b16ec30571096c5f1f0d9d2324bff31af97", size = 272833, upload-time = "2025-07-25T08:06:26.317Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -1910,15 +1903,15 @@ hiredis = [
|
||||
|
||||
[[package]]
|
||||
name = "reportlab"
|
||||
version = "4.4.6"
|
||||
version = "4.4.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "pillow" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/35/ec/f7a50b3cbee58407090bd1f2a9db2f1a23052c5de3bc7408024ca776ee02/reportlab-4.4.6.tar.gz", hash = "sha256:8792c87c23dd034d17530e6ebe4164d61bcc8f7b0eac203fe13cc03cc2c1c607", size = 3910805, upload-time = "2025-12-10T12:37:21.17Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/80/dfa85941e3c3800aa5cd2f940c1903358c1fb61149f5f91b62efa61e7d03/reportlab-4.4.5.tar.gz", hash = "sha256:0457d642aa76df7b36b0235349904c58d8f9c606a872456ed04436aafadc1510", size = 3910836, upload-time = "2025-11-18T11:43:10.242Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/ee/5f7a31ab05cf817e0cc70ae6df51a1a4fda188c899790a3131a24dd78d18/reportlab-4.4.6-py3-none-any.whl", hash = "sha256:c7c31d5c815bae7c76fc17f64ffc417e68992901acddb24504296cc39b065424", size = 1954259, upload-time = "2025-12-10T12:37:18.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/16/0c26a7bdfd20cba49a011b1095461be120c53df3926e9843fccfb9530e72/reportlab-4.4.5-py3-none-any.whl", hash = "sha256:849773d7cd5dde2072fedbac18c8bc909506c8befba8f088ba7b09243c6684cc", size = 1954256, upload-time = "2025-11-17T12:03:05.214Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1976,41 +1969,41 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.14.9"
|
||||
version = "0.14.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f6/1b/ab712a9d5044435be8e9a2beb17cbfa4c241aa9b5e4413febac2a8b79ef2/ruff-0.14.9.tar.gz", hash = "sha256:35f85b25dd586381c0cc053f48826109384c81c00ad7ef1bd977bfcc28119d5b", size = 5809165, upload-time = "2025-12-11T21:39:47.381Z" }
|
||||
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" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/1c/d1b1bba22cffec02351c78ab9ed4f7d7391876e12720298448b29b7229c1/ruff-0.14.9-py3-none-linux_armv6l.whl", hash = "sha256:f1ec5de1ce150ca6e43691f4a9ef5c04574ad9ca35c8b3b0e18877314aba7e75", size = 13576541, upload-time = "2025-12-11T21:39:14.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/ab/ffe580e6ea1fca67f6337b0af59fc7e683344a43642d2d55d251ff83ceae/ruff-0.14.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ed9d7417a299fc6030b4f26333bf1117ed82a61ea91238558c0268c14e00d0c2", size = 13779363, upload-time = "2025-12-11T21:39:20.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/f8/2be49047f929d6965401855461e697ab185e1a6a683d914c5c19c7962d9e/ruff-0.14.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d5dc3473c3f0e4a1008d0ef1d75cee24a48e254c8bed3a7afdd2b4392657ed2c", size = 12925292, upload-time = "2025-12-11T21:39:38.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/e9/08840ff5127916bb989c86f18924fd568938b06f58b60e206176f327c0fe/ruff-0.14.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84bf7c698fc8f3cb8278830fb6b5a47f9bcc1ed8cb4f689b9dd02698fa840697", size = 13362894, upload-time = "2025-12-11T21:39:02.524Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/1c/5b4e8e7750613ef43390bb58658eaf1d862c0cc3352d139cd718a2cea164/ruff-0.14.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa733093d1f9d88a5d98988d8834ef5d6f9828d03743bf5e338bf980a19fce27", size = 13311482, upload-time = "2025-12-11T21:39:17.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/3a/459dce7a8cb35ba1ea3e9c88f19077667a7977234f3b5ab197fad240b404/ruff-0.14.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a1cfb04eda979b20c8c19550c8b5f498df64ff8da151283311ce3199e8b3648", size = 14016100, upload-time = "2025-12-11T21:39:41.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/31/f064f4ec32524f9956a0890fc6a944e5cf06c63c554e39957d208c0ffc45/ruff-0.14.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1e5cb521e5ccf0008bd74d5595a4580313844a42b9103b7388eca5a12c970743", size = 15477729, upload-time = "2025-12-11T21:39:23.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/6d/f364252aad36ccd443494bc5f02e41bf677f964b58902a17c0b16c53d890/ruff-0.14.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd429a8926be6bba4befa8cdcf3f4dd2591c413ea5066b1e99155ed245ae42bb", size = 15122386, upload-time = "2025-12-11T21:39:33.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/02/e848787912d16209aba2799a4d5a1775660b6a3d0ab3944a4ccc13e64a02/ruff-0.14.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab208c1b7a492e37caeaf290b1378148f75e13c2225af5d44628b95fd7834273", size = 14497124, upload-time = "2025-12-11T21:38:59.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/51/0489a6a5595b7760b5dbac0dd82852b510326e7d88d51dbffcd2e07e3ff3/ruff-0.14.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72034534e5b11e8a593f517b2f2f2b273eb68a30978c6a2d40473ad0aaa4cb4a", size = 14195343, upload-time = "2025-12-11T21:39:44.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/53/3bb8d2fa73e4c2f80acc65213ee0830fa0c49c6479313f7a68a00f39e208/ruff-0.14.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:712ff04f44663f1b90a1195f51525836e3413c8a773574a7b7775554269c30ed", size = 14346425, upload-time = "2025-12-11T21:39:05.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/04/bdb1d0ab876372da3e983896481760867fc84f969c5c09d428e8f01b557f/ruff-0.14.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a111fee1db6f1d5d5810245295527cda1d367c5aa8f42e0fca9a78ede9b4498b", size = 13258768, upload-time = "2025-12-11T21:39:08.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/d9/8bf8e1e41a311afd2abc8ad12be1b6c6c8b925506d9069b67bb5e9a04af3/ruff-0.14.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8769efc71558fecc25eb295ddec7d1030d41a51e9dcf127cbd63ec517f22d567", size = 13326939, upload-time = "2025-12-11T21:39:53.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/56/a213fa9edb6dd849f1cfbc236206ead10913693c72a67fb7ddc1833bf95d/ruff-0.14.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:347e3bf16197e8a2de17940cd75fd6491e25c0aa7edf7d61aa03f146a1aa885a", size = 13578888, upload-time = "2025-12-11T21:39:35.988Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/09/6a4a67ffa4abae6bf44c972a4521337ffce9cbc7808faadede754ef7a79c/ruff-0.14.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7715d14e5bccf5b660f54516558aa94781d3eb0838f8e706fb60e3ff6eff03a8", size = 14314473, upload-time = "2025-12-11T21:39:50.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/0d/15cc82da5d83f27a3c6b04f3a232d61bc8c50d38a6cd8da79228e5f8b8d6/ruff-0.14.9-py3-none-win32.whl", hash = "sha256:df0937f30aaabe83da172adaf8937003ff28172f59ca9f17883b4213783df197", size = 13202651, upload-time = "2025-12-11T21:39:26.628Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/f7/c78b060388eefe0304d9d42e68fab8cffd049128ec466456cef9b8d4f06f/ruff-0.14.9-py3-none-win_amd64.whl", hash = "sha256:c0b53a10e61df15a42ed711ec0bda0c582039cf6c754c49c020084c55b5b0bc2", size = 14702079, upload-time = "2025-12-11T21:39:11.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/09/7a9520315decd2334afa65ed258fed438f070e31f05a2e43dd480a5e5911/ruff-0.14.9-py3-none-win_arm64.whl", hash = "sha256:8e821c366517a074046d92f0e9213ed1c13dbc5b37a7fc20b07f79b64d62cc84", size = 13744730, upload-time = "2025-12-11T21:39:29.659Z" },
|
||||
{ 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.47.0"
|
||||
version = "2.45.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4a/2a/d225cbf87b6c8ecce5664db7bcecb82c317e448e3b24a2dcdaacb18ca9a7/sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050", size = 381895, upload-time = "2025-12-03T14:06:36.846Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/89/1561b3dc8e28bf7978d031893297e89be266f53650c87bb14a29406a9791/sentry_sdk-2.45.0.tar.gz", hash = "sha256:e9bbfe69d5f6742f48bad22452beffb525bbc5b797d817c7f1b1f7d210cdd271", size = 373631, upload-time = "2025-11-18T13:23:22.475Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/ac/d6286ea0d49e7b58847faf67b00e56bb4ba3d525281e2ac306e1f1f353da/sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412", size = 411088, upload-time = "2025-12-03T14:06:35.374Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/c6/039121a0355bc1b5bcceef0dabf211b021fd435d0ee5c46393717bb1c09f/sentry_sdk-2.45.0-py2.py3-none-any.whl", hash = "sha256:86c8ab05dc3e8666aece77a5c747b45b25aa1d5f35f06cde250608f495d50f23", size = 404791, upload-time = "2025-11-18T13:23:20.533Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2095,8 +2088,8 @@ requires-dist = [
|
||||
{ name = "django-haystack", specifier = ">=3.3.0,<4.0.0" },
|
||||
{ name = "django-honeypot", specifier = ">=1.3.0,<2" },
|
||||
{ name = "django-jinja", specifier = ">=2.11.0,<3.0.0" },
|
||||
{ name = "django-ninja", specifier = ">=1.4.5,<2.0.0" },
|
||||
{ name = "django-ninja-extra", specifier = ">=0.30.2,<1.0.0" },
|
||||
{ name = "django-ninja", specifier = ">=1.5.0,<6.0.0" },
|
||||
{ name = "django-ninja-extra", specifier = ">=0.30.6" },
|
||||
{ name = "django-ordered-model", specifier = ">=3.7.4,<4.0.0" },
|
||||
{ name = "django-phonenumber-field", specifier = ">=8.3.0,<9.0.0" },
|
||||
{ name = "django-simple-captcha", specifier = ">=0.6.2,<1.0.0" },
|
||||
@@ -2258,11 +2251,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.4"
|
||||
version = "0.5.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/67/701f86b28d63b2086de47c942eccf8ca2208b3be69715a1119a4e384415a/sqlparse-0.5.4.tar.gz", hash = "sha256:4396a7d3cf1cd679c1be976cf3dc6e0a51d0111e87787e7a8d780e7d5a998f9e", size = 120112, upload-time = "2025-11-28T07:10:18.377Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/25/70/001ee337f7aa888fb2e3f5fd7592a6afc5283adb1ed44ce8df5764070f22/sqlparse-0.5.4-py3-none-any.whl", hash = "sha256:99a9f0314977b76d776a0fcb8554de91b9bb8a18560631d6bc48721d07023dcb", size = 45933, upload-time = "2025-11-28T07:10:19.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2352,32 +2345,20 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.3"
|
||||
version = "2025.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzlocal"
|
||||
version = "5.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.2"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user