Compare commits

..
Author SHA1 Message Date
dependabot[bot]andGitHub 235bcb9916 [UPDATE] Update sentry-sdk requirement
Updates the requirements on [sentry-sdk](https://github.com/getsentry/sentry-python) to permit the latest version.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.63.0...2.64.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.64.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-06 08:23:05 +00:00
17 changed files with 188 additions and 80 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.16.0 rev: v0.15.19
hooks: hooks:
- id: ruff-check # just check the code, and print the errors - id: ruff-check # just check the code, and print the errors
- id: ruff-check # actually fix the fixable errors, but print nothing - id: ruff-check # actually fix the fixable errors, but print nothing
+4 -8
View File
@@ -61,8 +61,9 @@ Un fichier de migration ressemble à ça :
```python ```python
from django.db import migrations from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
# liste des autres migrations à appliquer avant celle-ci # liste des autres migrations à appliquer avant celle-ci
] ]
@@ -116,16 +117,14 @@ Par exemple :
```python ```python
from django.db import migrations from django.db import migrations
def forwards_func(apps, schema_editor): def forwards_func(apps, schema_editor):
print("Appplication de la migration") print("Appplication de la migration")
def reverse_func(apps, schema_editor): def reverse_func(apps, schema_editor):
print("Annulation de la migration") print("Annulation de la migration")
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [] dependencies = []
operations = [ operations = [
@@ -165,7 +164,6 @@ On écrirait donc, dans l'application `pedagogy` :
from django.db import models from django.db import models
from core.models import User from core.models import User
class UserUe(models.Model): class UserUe(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
ue = models.CharField(max_length=10) ue = models.CharField(max_length=10)
@@ -176,7 +174,6 @@ Et nous aurions le fichier de migration suivant :
from django.db import migrations, models from django.db import migrations, models
from django.conf import settings from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
@@ -292,7 +289,6 @@ La commande vous donnera ceci :
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
replaces = [("pedagogy", "0004_userue"), ("pedagogy", "0005_alter_userue_ue")] replaces = [("pedagogy", "0004_userue"), ("pedagogy", "0005_alter_userue_ue")]
+5 -1
View File
@@ -191,6 +191,7 @@ for user in richest.annotate(amount=F("customer__amount"))[:100]:
On aurait même pu réorganiser ça : On aurait même pu réorganiser ça :
```python ```python
from core.models import User from core.models import User
from django.db.models import F from django.db.models import F
@@ -238,7 +239,10 @@ nous écrivons donc instinctivement :
from counter.models import Counter from counter.models import Counter
foyer = Counter.objects.get(name="Foyer") foyer = Counter.objects.get(name="Foyer")
total_amount = sum(sale.amount * sale.unit_price for sale in foyer.sellings.all()) total_amount = sum(
sale.amount * sale.unit_price
for sale in foyer.sellings.all()
)
``` ```
On pourrait penser qu'il n'y a pas de problème. On pourrait penser qu'il n'y a pas de problème.
+2 -1
View File
@@ -27,11 +27,12 @@ SITH_SUBSCRIPTIONS = {
"cursus-alternant": {"name": _("Alternating cursus"), "price": 30, "duration": 6}, "cursus-alternant": {"name": _("Alternating cursus"), "price": 30, "duration": 6},
"membre-honoraire": {"name": _("Honorary member"), "price": 0, "duration": 666}, "membre-honoraire": {"name": _("Honorary member"), "price": 0, "duration": 666},
"un-jour": {"name": _("One day"), "price": 0, "duration": 0.00555333}, "un-jour": {"name": _("One day"), "price": 0, "duration": 0.00555333},
# On rajoute ici notre cotisation # On rajoute ici notre cotisation
# Elle se nomme "Un mois" # Elle se nomme "Un mois"
# Coûte 6€ # Coûte 6€
# Dure 1 mois (on raisonne en semestre, ici, c'est 1/6 de semestre) # Dure 1 mois (on raisonne en semestre, ici, c'est 1/6 de semestre)
"un-mois": {"name": _("One month"), "price": 6, "duration": 0.166}, "un-mois": {"name": _("One month"), "price": 6, "duration": 0.166}
} }
``` ```
+1 -1
View File
@@ -15,7 +15,7 @@ Si le mot est dans le code Python :
```python ```python
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
help_text = _("Hello") help_text=_("Hello")
``` ```
Si le mot apparaît dans le template Jinja : Si le mot apparaît dans le template Jinja :
-1
View File
@@ -26,7 +26,6 @@ from django.templatetags.static import static
# Sélectionnez le fichier de bannière pour le weekmail de l'automne 2018 # Sélectionnez le fichier de bannière pour le weekmail de l'automne 2018
def get_banner(self): def get_banner(self):
return "http://" + settings.SITH_URL + static("com/img/weekmail_bannerA18.jpg") return "http://" + settings.SITH_URL + static("com/img/weekmail_bannerA18.jpg")
``` ```
+2 -3
View File
@@ -147,15 +147,14 @@ Voici quelques exemples :
env = Env() env = Env()
env.read_env() env.read_env()
async def main(): async def main():
async with aiohttp.ClientSession( async with aiohttp.ClientSession(
base_url="https://ae.utbm.fr/api/", headers={"X-APIKey": env.str("API_KEY")} base_url="https://ae.utbm.fr/api/",
headers={"X-APIKey": env.str("API_KEY")}
) as session: ) as session:
async with session.get("club/1") as res: async with session.get("club/1") as res:
print(await res.json()) print(await res.json())
asyncio.run(main()) asyncio.run(main())
``` ```
+11 -7
View File
@@ -161,8 +161,8 @@ qui seront alors injectés.
```python ```python
from django.views.generic import CreateView, UpdateView, TemplateView from django.views.generic import CreateView, UpdateView, TemplateView
from core.views.mixins import FragmentMixin from core.views.mixins import FragmentMixin
class FooCreateFragment(FragmentMixin, CreateView): class FooCreateFragment(FragmentMixin, CreateView):
model = Foo model = Foo
fields = ["foo", "bar"] fields = ["foo", "bar"]
@@ -182,7 +182,7 @@ qui seront alors injectés.
def get_context_data(**kwargs): def get_context_data(**kwargs):
return super().get_context_data(**kwargs) | { return super().get_context_data(**kwargs) | {
"create_fragment": FooCreateFragment.as_fragment()(), "create_fragment": FooCreateFragment.as_fragment()(),
"update_fragment": FooUpdateFragment.as_fragment()(foo_id=1), "update_fragment": FooUpdateFragment.as_fragment()(foo_id=1)
} }
``` ```
@@ -288,7 +288,7 @@ class FooCreateFragment(FragmentMixin, CreateView):
def render_fragment(self, request, **kwargs): def render_fragment(self, request, **kwargs):
if "foo" in kwargs: if "foo" in kwargs:
kwargs["foo"] += 2 kwargs["foo"] += 2
return super().render_fragment(request, **kwargs) return super().render_fragment(request, **kwargs)
``` ```
@@ -319,9 +319,11 @@ from core.views.mixins import UseFragmentsMixin
class FooCompositeFormView(UseFragmentsMixin, TemplateView): class FooCompositeFormView(UseFragmentsMixin, TemplateView):
fragments = { fragments = {
"create_fragment": FooCreateFragment, "create_fragment": FooCreateFragment,
"update_fragment": FooUpdateFragment, "update_fragment": FooUpdateFragment
}
fragment_data = {
"update_fragment": {"foo": 4}
} }
fragment_data = {"update_fragment": {"foo": 4}}
template_name = "app/foo.jinja" template_name = "app/foo.jinja"
``` ```
@@ -340,7 +342,9 @@ from core.views.mixins import UseFragmentsMixin
class FooCompositeFormView(UseFragmentsMixin, TemplateView): class FooCompositeFormView(UseFragmentsMixin, TemplateView):
fragments = {"create_fragment": FooCreateFragment} fragments = {
"create_fragment": FooCreateFragment
}
template_name = "app/foo.jinja" template_name = "app/foo.jinja"
def get_fragment_context_data(self): def get_fragment_context_data(self):
+12 -14
View File
@@ -123,7 +123,6 @@ Par exemple, prenons le modèle suivant :
```python ```python
from django.db import models from django.db import models
class News(models.Model): class News(models.Model):
# ... # ...
@@ -193,21 +192,20 @@ Pour les vues sous forme de fonction, il y a le décorateur
```python ```python
from com.models import News from com.models import News
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.views import View from django.views import View
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
class NewsModerateView(PermissionRequiredMixin, SingleObjectMixin, View): class NewsModerateView(PermissionRequiredMixin, SingleObjectMixin, View):
model = News model = News
pk_url_kwarg = "news_id" pk_url_kwarg = "news_id"
permission_required = "com.moderate_news" permission_required = "com.moderate_news"
# On peut aussi fournir plusieurs permissions, par exemple : # On peut aussi fournir plusieurs permissions, par exemple :
# permission_required = ["com.moderate_news", "com.delete_news"] # permission_required = ["com.moderate_news", "com.delete_news"]
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
# Si nous sommes ici, nous pouvons être certains que l'utilisateur # Si nous sommes ici, nous pouvons être certains que l'utilisateur
# a la permission requise # a la permission requise
@@ -221,13 +219,12 @@ Pour les vues sous forme de fonction, il y a le décorateur
```python ```python
from com.models import News from com.models import News
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
@permission_required("com.moderate_news") @permission_required("com.moderate_news")
@require_POST @require_POST
def moderate_news(request, news_id: int): def moderate_news(request, news_id: int):
@@ -266,7 +263,6 @@ from core.auth.mixins import PermissionOrAuthorRequiredMixin
from django.views.generic import UpdateView from django.views.generic import UpdateView
class NewsUpdateView(PermissionOrAuthorRequiredMixin, UpdateView): class NewsUpdateView(PermissionOrAuthorRequiredMixin, UpdateView):
model = News model = News
pk_url_kwarg = "news_id" pk_url_kwarg = "news_id"
@@ -328,8 +324,8 @@ Voici un exemple d'implémentation de ce système :
from core.models import User, Group from core.models import User, Group
class Article(models.Model): class Article(models.Model):
title = models.CharField(_("title"), max_length=100) title = models.CharField(_("title"), max_length=100)
content = models.TextField(_("content")) content = models.TextField(_("content"))
@@ -374,7 +370,6 @@ Voici un exemple d'implémentation de ce système :
from core.models import User, Group from core.models import User, Group
class Article(models.Model): class Article(models.Model):
title = models.CharField(_("title"), max_length=100) title = models.CharField(_("title"), max_length=100)
content = models.TextField(_("content")) content = models.TextField(_("content"))
@@ -383,7 +378,7 @@ Voici un exemple d'implémentation de ce système :
owner_group = models.ForeignKey( # (1)! owner_group = models.ForeignKey( # (1)!
Group, related_name="owned_articles", default=settings.SITH_GROUP_ROOT_ID Group, related_name="owned_articles", default=settings.SITH_GROUP_ROOT_ID
) )
# relation many-to-many # relation many-to-many
edit_groups = models.ManyToManyField( # (2)! edit_groups = models.ManyToManyField( # (2)!
Group, Group,
@@ -391,7 +386,7 @@ Voici un exemple d'implémentation de ce système :
verbose_name=_("edit groups"), verbose_name=_("edit groups"),
blank=True, blank=True,
) )
# relation many-to-many # relation many-to-many
view_groups = models.ManyToManyField( # (3)! view_groups = models.ManyToManyField( # (3)!
Group, Group,
@@ -534,7 +529,10 @@ class NewsQuerySet(models.QuerySet): # (1)!
return self return self
# sinon, on retourne les nouvelles modérées ou dont l'utilisateur # sinon, on retourne les nouvelles modérées ou dont l'utilisateur
# est l'auteur # est l'auteur
return self.filter(models.Q(is_moderated=True) | models.Q(author=user)) return self.filter(
models.Q(is_moderated=True)
| models.Q(author=user)
)
class News(models.Model): class News(models.Model):
@@ -148,6 +148,56 @@
</span> </span>
</div> </div>
{% endif %} {% endif %}
<section>
<div class="category-header">
<h3 class="margin-bottom">{% trans %}Eurockéennes 2025 partnership{% endtrans %}</h3>
{% if user.is_subscribed %}
<div id="eurock-partner" style="
min-height: 600px;
background-color: lightgrey;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 10px;
">
<p style="text-align: center;">
{% trans trimmed %}
Our partner uses Weezevent to sell tickets.
Weezevent may collect user info according to
its own privacy policy.
By clicking the accept button you consent to
their terms of services.
{% endtrans %}
</p>
<a href="https://weezevent.com/fr/politique-de-confidentialite/">{% trans %}Privacy policy{% endtrans %}</a>
<button
hx-get="{{ url("eboutic:eurock") }}"
hx-target="#eurock-partner"
hx-swap="outerHTML"
hx-trigger="click, load[document.cookie.includes('weezevent_accept=true')]"
@htmx:after-request="document.cookie = 'weezevent_accept=true'"
>{% trans %}Accept{% endtrans %}
</button>
</div>
{% else %}
<p>
{%- trans trimmed %}
You must be subscribed to benefit from the partnership with the Eurockéennes.
{% endtrans -%}
</p>
<p>
{%- trans trimmed %}
This partnership offers a discount of up to 33%
on tickets for Friday, Saturday and Sunday,
as well as the 3-day package from Friday to Sunday.
{% endtrans -%}
</p>
{% endif %}
</div>
</section>
{% for prices in categories %} {% for prices in categories %}
{% set category = prices[0].product.product_type %} {% set category = prices[0].product.product_type %}
<section> <section>
@@ -0,0 +1,16 @@
<a title="Logiciel billetterie en ligne"
href="https://www.weezevent.com?c=sys_widget"
class="weezevent-widget-integration"
target="_blank"
data-src="https://widget.weezevent.com/ticket/8aaba226-f7a3-4192-a64e-72ff8f5b35b7?id_evenement=1419869&locale=fr-FR&code=28747"
data-width="650"
data-height="600"
data-resize="1"
data-nopb="0"
data-type="neo"
data-width_auto="1"
data-noscroll="0"
data-id="1419869">
Billetterie Weezevent
</a>
<script type="text/javascript" src="https://widget.weezevent.com/weez.js" async defer></script>
+2
View File
@@ -31,6 +31,7 @@ from eboutic.views import (
EbouticMainView, EbouticMainView,
EbouticPayWithSith, EbouticPayWithSith,
EtransactionAutoAnswer, EtransactionAutoAnswer,
EurockPartnerFragment,
payment_result, payment_result,
) )
@@ -50,4 +51,5 @@ urlpatterns = [
EtransactionAutoAnswer.as_view(), EtransactionAutoAnswer.as_view(),
name="etransation_autoanswer", name="etransation_autoanswer",
), ),
path("eurock/", EurockPartnerFragment.as_view(), name="eurock"),
] ]
+6 -2
View File
@@ -43,11 +43,11 @@ from django.utils.formats import localize
from django.utils.timezone import localtime from django.utils.timezone import localtime
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_GET from django.views.decorators.http import require_GET
from django.views.generic import DetailView, FormView, UpdateView, View from django.views.generic import DetailView, FormView, TemplateView, UpdateView, View
from django.views.generic.edit import SingleObjectMixin from django.views.generic.edit import SingleObjectMixin
from django_countries.fields import Country from django_countries.fields import Country
from core.auth.mixins import CanViewMixin from core.auth.mixins import CanViewMixin, IsSubscriberMixin
from core.views.mixins import FragmentMixin, UseFragmentsMixin from core.views.mixins import FragmentMixin, UseFragmentsMixin
from counter.forms import BaseBasketForm, BasketItemForm, BillingInfoForm from counter.forms import BaseBasketForm, BasketItemForm, BillingInfoForm
from counter.models import ( from counter.models import (
@@ -359,3 +359,7 @@ class EtransactionAutoAnswer(View):
return HttpResponse( return HttpResponse(
"Payment failed with error: " + request.GET["Error"], status=202 "Payment failed with error: " + request.GET["Error"], status=202
) )
class EurockPartnerFragment(IsSubscriberMixin, TemplateView):
template_name = "eboutic/eurock_fragment.jinja"
+41 -4
View File
@@ -4465,6 +4465,47 @@ msgstr ""
msgid "this page" msgid "this page"
msgstr "cette page" msgstr "cette page"
#: eboutic/templates/eboutic/eboutic_main.jinja
msgid "Eurockéennes 2025 partnership"
msgstr "Partenariat Eurockéennes 2025"
#: eboutic/templates/eboutic/eboutic_main.jinja
msgid ""
"Our partner uses Weezevent to sell tickets. Weezevent may collect user info "
"according to its own privacy policy. By clicking the accept button you "
"consent to their terms of services."
msgstr ""
"Notre partenaire utilises Wezevent pour vendre ses billets. Weezevent peut "
"collecter des informations utilisateur conformément à sa propre politique de "
"confidentialité. En cliquant sur le bouton d'acceptation vous consentez à "
"leurs termes de service."
#: eboutic/templates/eboutic/eboutic_main.jinja
msgid "Privacy policy"
msgstr "Politique de confidentialité"
#: eboutic/templates/eboutic/eboutic_main.jinja
#: trombi/templates/trombi/comment_moderation.jinja
msgid "Accept"
msgstr "Accepter"
#: eboutic/templates/eboutic/eboutic_main.jinja
msgid ""
"You must be subscribed to benefit from the partnership with the Eurockéennes."
msgstr ""
"Vous devez être cotisant pour bénéficier du partenariat avec les "
"Eurockéennes."
#: eboutic/templates/eboutic/eboutic_main.jinja
#, python-format
msgid ""
"This partnership offers a discount of up to 33%% on tickets for Friday, "
"Saturday and Sunday, as well as the 3-day package from Friday to Sunday."
msgstr ""
"Ce partenariat permet de profiter d'une réduction jusqu'à 33%% sur les "
"billets du vendredi, du samedi et du dimanche, ainsi qu'au forfait 3 jours, "
"du vendredi au dimanche."
#: eboutic/templates/eboutic/eboutic_main.jinja #: eboutic/templates/eboutic/eboutic_main.jinja
msgid "Product sold out" msgid "Product sold out"
msgstr "Produit épuisé" msgstr "Produit épuisé"
@@ -5908,10 +5949,6 @@ msgstr "fin"
msgid "Moderate Trombi comments" msgid "Moderate Trombi comments"
msgstr "Modérer les commentaires du Trombi" msgstr "Modérer les commentaires du Trombi"
#: trombi/templates/trombi/comment_moderation.jinja
msgid "Accept"
msgstr "Accepter"
#: trombi/templates/trombi/comment_moderation.jinja #: trombi/templates/trombi/comment_moderation.jinja
msgid "Reject" msgid "Reject"
msgstr "Refuser" msgstr "Refuser"
+2 -2
View File
@@ -35,7 +35,7 @@ dependencies = [
"django-ordered-model>=3.7.4,<4.0.0", "django-ordered-model>=3.7.4,<4.0.0",
"django-simple-captcha>=0.6.3,<1.0.0", "django-simple-captcha>=0.6.3,<1.0.0",
"python-dateutil>=2.9.0.post0,<3.0.0.0", "python-dateutil>=2.9.0.post0,<3.0.0.0",
"sentry-sdk>=2.63.0,<3.0.0", "sentry-sdk>=2.64.0,<3.0.0",
"jinja2>=3.1.6,<4.0.0", "jinja2>=3.1.6,<4.0.0",
"django-countries>=9.0.0,<10.0.0", "django-countries>=9.0.0,<10.0.0",
"dict2xml>=1.7.8,<2.0.0", "dict2xml>=1.7.8,<2.0.0",
@@ -66,7 +66,7 @@ dev = [
"django-debug-toolbar>=7.0.0,<8", "django-debug-toolbar>=7.0.0,<8",
"ipython>=9.14.1,<10.0.0", "ipython>=9.14.1,<10.0.0",
"pre-commit>=4.6.0,<5.0.0", "pre-commit>=4.6.0,<5.0.0",
"ruff>=0.16.0,<1.0.0", "ruff>=0.15.19,<1.0.0",
"djhtml>=3.0.11,<4.0.0", "djhtml>=3.0.11,<4.0.0",
"faker>=40.23.0,<41.0.0", "faker>=40.23.0,<41.0.0",
"rjsmin>=1.2.5,<2.0.0", "rjsmin>=1.2.5,<2.0.0",
+7 -9
View File
@@ -82,11 +82,7 @@
<div class="container" id="pict"> <div class="container" id="pict">
<div class="main"> <div class="main">
<div <div class="photo" :aria-busy="currentPicture.imageLoading">
class="photo"
:aria-busy="currentPicture.imageLoading"
@click="currentPicture = (($event.offsetX > $el.offsetWidth / 2) ? nextPicture : previousPicture) ?? currentPicture"
>
<img <img
:src="currentPicture.compressedUrl" :src="currentPicture.compressedUrl"
:alt="currentPicture.name" :alt="currentPicture.name"
@@ -99,11 +95,13 @@
<div class="infos"> <div class="infos">
<h5>{% trans %}Infos{% endtrans %}</h5> <h5>{% trans %}Infos{% endtrans %}</h5>
<div> <div>
<div <div>
x-data="{formatter: Intl.DateTimeFormat('{{ LANGUAGE_CODE }}', {dateStyle: 'long'})}"
>
<span>{% trans %}Date: {% endtrans %}</span> <span>{% trans %}Date: {% endtrans %}</span>
<span x-text="formatter.format(new Date(currentPicture.date))"> <span
x-text="Intl.DateTimeFormat(
'{{ LANGUAGE_CODE }}', {dateStyle: 'long'}
).format(Date.parse(currentPicture.date))"
>
</span> </span>
</div> </div>
<div> <div>
Generated
+26 -26
View File
@@ -972,9 +972,9 @@ resolution-markers = [
"python_full_version < '3.13'", "python_full_version < '3.13'",
] ]
dependencies = [ dependencies = [
{ name = "pydantic" }, { name = "pydantic", marker = "python_full_version < '3.13'" },
{ name = "python-dateutil" }, { name = "python-dateutil", marker = "python_full_version < '3.13'" },
{ name = "tzdata" }, { name = "tzdata", marker = "python_full_version < '3.13'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0c/a3/506fe8d9416472588ff30d4c97a7e477f680a098c74a9f2050bd84182a75/ical-12.0.0.tar.gz", hash = "sha256:2f1c7450bd1eff586322273360f44931eb0919dc048c7284f1647037624be7c4", size = 125412, upload-time = "2025-11-13T06:00:04.95Z" } sdist = { url = "https://files.pythonhosted.org/packages/0c/a3/506fe8d9416472588ff30d4c97a7e477f680a098c74a9f2050bd84182a75/ical-12.0.0.tar.gz", hash = "sha256:2f1c7450bd1eff586322273360f44931eb0919dc048c7284f1647037624be7c4", size = 125412, upload-time = "2025-11-13T06:00:04.95Z" }
wheels = [ wheels = [
@@ -989,9 +989,9 @@ resolution-markers = [
"python_full_version >= '3.13'", "python_full_version >= '3.13'",
] ]
dependencies = [ dependencies = [
{ name = "pydantic" }, { name = "pydantic", marker = "python_full_version >= '3.13'" },
{ name = "python-dateutil" }, { name = "python-dateutil", marker = "python_full_version >= '3.13'" },
{ name = "tzdata" }, { name = "tzdata", marker = "python_full_version >= '3.13'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f5/1c/dc7378c5ba63e8b0f2987bd827a1e40bd27196d37a7082be0593551e8ed8/ical-13.2.5.tar.gz", hash = "sha256:1cc3116e6f522eeeaa694b3e20b58dbfbe4b281954a8d3d28892cfb61f03fd40", size = 131405, upload-time = "2026-05-24T16:51:56.017Z" } sdist = { url = "https://files.pythonhosted.org/packages/f5/1c/dc7378c5ba63e8b0f2987bd827a1e40bd27196d37a7082be0593551e8ed8/ical-13.2.5.tar.gz", hash = "sha256:1cc3116e6f522eeeaa694b3e20b58dbfbe4b281954a8d3d28892cfb61f03fd40", size = 131405, upload-time = "2026-05-24T16:51:56.017Z" }
wheels = [ wheels = [
@@ -2074,27 +2074,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.0" version = "0.15.19"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" } sdist = { url = "https://files.pythonhosted.org/packages/d5/e6/15800dfde183a1a106594016c912b4c12d050a301989d1aca6cb63759fe8/ruff-0.15.19.tar.gz", hash = "sha256:edc27f7172a93b32b102687009d6a588508815072141543ae603a8b9b0823063", size = 4772071, upload-time = "2026-06-24T01:10:46.942Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" }, { url = "https://files.pythonhosted.org/packages/88/4c/9ded7626c39a0440c575bf69e2bf500d443388272c842662c59852ee7fcd/ruff-0.15.19-py3-none-linux_armv6l.whl", hash = "sha256:922d1eb283161564759bd49f507e91dc6112c15da8bd5b84ed714e086243cf86", size = 10950859, upload-time = "2026-06-24T01:10:38.491Z" },
{ url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" }, { url = "https://files.pythonhosted.org/packages/fb/ef/c211505ece1d00ef493d58e54e3b6383c946a21e9874774eb531f2512cf3/ruff-0.15.19-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4d190d8f62a0b94aba8f721116538a9ee29b1e74d26650846ba9b99f0ae21c40", size = 11294529, upload-time = "2026-06-24T01:10:36.481Z" },
{ url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" }, { url = "https://files.pythonhosted.org/packages/fe/93/78d462e7d39968e58094dc57be7d09ffb14ce37da5b68ed70338a35a1f21/ruff-0.15.19-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a2c86ba6870dd415a9d9eb8be94d7924ebec6a26ffc7958ec7ca29d4bff967d", size = 10641416, upload-time = "2026-06-24T01:10:48.923Z" },
{ url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" }, { url = "https://files.pythonhosted.org/packages/76/c4/5cb66cfd1f865d5cca908b86c93ac785e7f572193d3c7426079ca6643e24/ruff-0.15.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b432bc087264aea70fd25ac198918b70bd9e2aa0db4297b0bb91bbfbbc63ce", size = 11015582, upload-time = "2026-06-24T01:10:30.089Z" },
{ url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" }, { url = "https://files.pythonhosted.org/packages/51/9f/8ecfaec10cf5eecd28fbc00ff4fb867db90a1be54bf3d39ebf93f893cd52/ruff-0.15.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8530a09d03b3a8c994f8b559a7dcdabc690bcd3f78ef276c38c83166798ebf56", size = 10744059, upload-time = "2026-06-24T01:10:32.48Z" },
{ url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" }, { url = "https://files.pythonhosted.org/packages/35/6b/983249d04562bc2d590edd75f32455cdb473affb3ba4bc8d883e939c697d/ruff-0.15.19-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87bf21fb3875fe69f0eacc825411657e2e85589cce633c35c0adf1113649c62b", size = 11568461, upload-time = "2026-06-24T01:10:17.435Z" },
{ url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" }, { url = "https://files.pythonhosted.org/packages/eb/39/bc7794f127b18f492a3b4ee82bba5a900c985ff13b72b46f46e3c171ba34/ruff-0.15.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b229cb3ef56ecc2c1c8ebeca64b7a7740ccaef40a9eb097e78dde5a8560b83", size = 12429690, upload-time = "2026-06-24T01:10:40.638Z" },
{ url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" }, { url = "https://files.pythonhosted.org/packages/0a/3b/0de6859e698ed11c8a49e765196c8d333599b6a546c0715df39b6ba1aa2e/ruff-0.15.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c754515be7b76afe6e7e62df7776709571bcfc1631183828afcf3bafa869e3", size = 11693067, upload-time = "2026-06-24T01:10:25.681Z" },
{ url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" }, { url = "https://files.pythonhosted.org/packages/89/3d/0b1f30f84bee9ae6ae8d349c2ba8b6f4b040966744efdd3acc804ae7c024/ruff-0.15.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a498f82e0f4d8904c4e0aea5139cdfac1f39d19a3c51d491292f63a36e83b2e", size = 11616911, upload-time = "2026-06-24T01:10:44.809Z" },
{ url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" }, { url = "https://files.pythonhosted.org/packages/4d/eb/c90bd3dfc12eed9032c2c1bfe05105b93a1b2c8bce555db6308315b853ce/ruff-0.15.19-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d48caa34488fb521fd0ef4aea2b0e8fe758298df044138f0d67b687a6a0d07ed", size = 11649343, upload-time = "2026-06-24T01:10:23.472Z" },
{ url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" }, { url = "https://files.pythonhosted.org/packages/82/91/01caa13602a2f12fae5edbe8caf78b3c1e6db1293132aee6959eecce095c/ruff-0.15.19-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4171b6613effa9363cd46dd4f75bd1827b6d1b946b5e278ed0c600d305379445", size = 10977610, upload-time = "2026-06-24T01:10:50.892Z" },
{ url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" }, { url = "https://files.pythonhosted.org/packages/3c/51/acb817922feab9ecbb3201377d4dbe7a25f1395e46545820061973f03468/ruff-0.15.19-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27c15b2a241dd4d995557949a094fe78b8ad99122a38ccae1595849bcc947b3f", size = 10744900, upload-time = "2026-06-24T01:10:42.726Z" },
{ url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" }, { url = "https://files.pythonhosted.org/packages/84/bc/5c8ca46b8a7a3f2b16cfbec88721d772b1c93912904e8f8c2e49470fea63/ruff-0.15.19-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ed03b7862d68f0a8771d50ee129980cbf1b113f96e250b73954bc292f689e0bb", size = 11293560, upload-time = "2026-06-24T01:10:21.262Z" },
{ url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" }, { url = "https://files.pythonhosted.org/packages/81/e0/4a888cbe4d5523b3f77a2b1fa043f46cfeba1b32eac35dcfadee0578fa8a/ruff-0.15.19-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:08143f0685ae278b30727ea72e90c61e5bd9c31b91aac4f5bb989538f73d24b8", size = 11696533, upload-time = "2026-06-24T01:10:53.046Z" },
{ url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" }, { url = "https://files.pythonhosted.org/packages/98/43/c34b2fcd79262a85161764a97aaca89c3e4f574340ab61430cefa2bdd2c1/ruff-0.15.19-py3-none-win32.whl", hash = "sha256:8f47f0f92952af2557212bb10cf3e695cd4cf28b2c6e42cdb18ec6c9ebfa19da", size = 10986299, upload-time = "2026-06-24T01:10:55.185Z" },
{ url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" }, { url = "https://files.pythonhosted.org/packages/22/e8/15fd23e02b2442b56b2026b455977bc3057aa34b26e6323d1e99e8531a9f/ruff-0.15.19-py3-none-win_amd64.whl", hash = "sha256:efeca47ee3f9d4a7162655a3b8e6ee4a878646044233978d4d2c1ff8cdd914f0", size = 12123473, upload-time = "2026-06-24T01:10:27.74Z" },
{ url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" }, { url = "https://files.pythonhosted.org/packages/30/66/9a73695e31eaee04f35d8475998bf8ab354465f9c638936d76111603dcc5/ruff-0.15.19-py3-none-win_arm64.whl", hash = "sha256:6c6b607466e47349332eb1d9be52fb1467423fc07c217341af41cd0f3f0573be", size = 11376779, upload-time = "2026-06-24T01:10:34.465Z" },
] ]
[[package]] [[package]]
@@ -2226,7 +2226,7 @@ dev = [
{ name = "ipython", specifier = ">=9.14.1,<10.0.0" }, { name = "ipython", specifier = ">=9.14.1,<10.0.0" },
{ name = "pre-commit", specifier = ">=4.6.0,<5.0.0" }, { name = "pre-commit", specifier = ">=4.6.0,<5.0.0" },
{ name = "rjsmin", specifier = ">=1.2.5,<2.0.0" }, { name = "rjsmin", specifier = ">=1.2.5,<2.0.0" },
{ name = "ruff", specifier = ">=0.16.0,<1.0.0" }, { name = "ruff", specifier = ">=0.15.19,<1.0.0" },
] ]
docs = [ docs = [
{ name = "mkdocs", specifier = ">=1.6.1,<2.0.0" }, { name = "mkdocs", specifier = ">=1.6.1,<2.0.0" },