From 4f73edaa37f4f4d21436e36dae4198231a59ed41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 6 Dec 2023 11:49:48 +0100 Subject: [PATCH 001/213] fix: sqlalchemy_utils EncryptedType deprecation --- web/flaskr/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/flaskr/models.py b/web/flaskr/models.py index 134e444d..00c8e538 100755 --- a/web/flaskr/models.py +++ b/web/flaskr/models.py @@ -23,7 +23,7 @@ from flask_sqlalchemy import SQLAlchemy from flaskr.tasks import background_upload from flaskr.utils import secret_key -from sqlalchemy_utils import EncryptedType +from sqlalchemy_utils import StringEncryptedType db = SQLAlchemy() @@ -585,8 +585,8 @@ class Meeting(db.Model): # BBB params name = db.Column(db.Unicode(150)) - attendeePW = db.Column(EncryptedType(db.Unicode(50), secret_key())) - moderatorPW = db.Column(EncryptedType(db.Unicode(50), secret_key())) + attendeePW = db.Column(StringEncryptedType(db.Unicode(50), secret_key())) + moderatorPW = db.Column(StringEncryptedType(db.Unicode(50), secret_key())) welcome = db.Column(db.UnicodeText()) dialNumber = db.Column(db.Unicode(50)) voiceBridge = db.Column(db.Unicode(50)) From 783b616adb99940f7d81451c2a41bfd3b90b2042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 6 Dec 2023 14:27:53 +0100 Subject: [PATCH 002/213] refactor: use 'url_for' in templates --- web/flaskr/templates/brand.html | 2 +- web/flaskr/templates/errors/400.html | 2 +- web/flaskr/templates/errors/403.html | 2 +- web/flaskr/templates/errors/404.html | 2 +- web/flaskr/templates/footer.html | 8 ++--- .../templates/footer/documentation.html | 36 +++++++++---------- web/flaskr/templates/jumbotron.html | 2 +- web/flaskr/templates/meeting/files.html | 8 ++--- web/flaskr/templates/meeting/jumbotron.html | 2 +- web/flaskr/templates/meeting/list.html | 2 +- web/flaskr/templates/meeting/recordings.html | 4 +-- web/flaskr/templates/meeting/submit.html | 2 +- web/flaskr/templates/redirections.html | 2 +- web/flaskr/templates/rie.html | 2 +- web/flaskr/templates/tools.html | 6 ++-- 15 files changed, 41 insertions(+), 41 deletions(-) diff --git a/web/flaskr/templates/brand.html b/web/flaskr/templates/brand.html index d1f0b38b..0f8903ac 100644 --- a/web/flaskr/templates/brand.html +++ b/web/flaskr/templates/brand.html @@ -13,7 +13,7 @@ diff --git a/web/flaskr/templates/errors/403.html b/web/flaskr/templates/errors/403.html index c0d4c6ae..ae53bacd 100644 --- a/web/flaskr/templates/errors/403.html +++ b/web/flaskr/templates/errors/403.html @@ -5,7 +5,7 @@

{% trans %}Erreur 403{% endtrans %}

-

{% trans %}Vous n’êtes pas autorisé à accéder cette page. Vous pouvez retourner à l’accueil.{% endtrans %}

+

{% trans index_url=url_for("routes.index") %}Vous n’êtes pas autorisé à accéder cette page. Vous pouvez retourner à l’accueil.{% endtrans %}

diff --git a/web/flaskr/templates/errors/404.html b/web/flaskr/templates/errors/404.html index 1306577e..9d450bda 100644 --- a/web/flaskr/templates/errors/404.html +++ b/web/flaskr/templates/errors/404.html @@ -5,7 +5,7 @@

{% trans %}Erreur 404{% endtrans %}

-

{% trans %}Cette page n'existe pas. Vous pouvez retourner à l’accueil.{% endtrans %}

+

{% trans index_url=url_for("routes.index") %}Cette page n'existe pas. Vous pouvez retourner à l’accueil.{% endtrans %}

diff --git a/web/flaskr/templates/footer.html b/web/flaskr/templates/footer.html index ed88b0ab..9098be2b 100644 --- a/web/flaskr/templates/footer.html +++ b/web/flaskr/templates/footer.html @@ -35,16 +35,16 @@ diff --git a/web/flaskr/templates/footer/documentation.html b/web/flaskr/templates/footer/documentation.html index 98180f22..13d6f4d0 100644 --- a/web/flaskr/templates/footer/documentation.html +++ b/web/flaskr/templates/footer/documentation.html @@ -6,25 +6,25 @@

{% trans %}Guides utilisateurs{% endtrans %}

{% if documentation_page_subtitle %}

{{ documentation_page_subtitle }}

{% endif %} -
  • {% trans %}Présentation des nouveautés de la v2.4{% endtrans %}
  • +
  • {% trans %}Présentation des nouveautés de la v2.4{% endtrans %}


  • -
  • {% trans %}Créer {{ an_improvised_meeting }}{% endtrans %}
  • -
  • {% trans %}Participer à {{ a_meeting }}{% endtrans %}
  • -
  • {% trans %}Créer et paramétrer une salle de {{ meeting_label }}{% endtrans %}
  • -
  • {% trans %}Activer le matériel dans le navigateur{% endtrans %}
  • -
  • {% trans %}Gérer les utilisateurs{% endtrans %}
  • -
  • {% trans %}Gérer les documents{% endtrans %}
  • -
  • {% trans %}Gestion des {{ private_meetings }}{% endtrans %}
  • -
  • {% trans %}Déléguer la création de {{ meeting_label }}{% endtrans %}
  • -
  • {% trans %}Enregistrer {{ a_meeting }}{% endtrans %}
  • -
  • {% trans %}Créer un sondage{% endtrans %}
  • -
  • {% trans %}Joindre {{ a_meeting }} par téléphone{% endtrans %}
  • -
  • {% trans %}Afficher le tableau de bord{% endtrans %}
  • -
  • {% trans %}Gérer la mise en page{% endtrans %}
  • -
  • {% trans %}Contrôle du volume{% endtrans %}
  • +
  • {% trans %}Créer {{ an_improvised_meeting }}{% endtrans %}
  • +
  • {% trans %}Participer à {{ a_meeting }}{% endtrans %}
  • +
  • {% trans %}Créer et paramétrer une salle de {{ meeting_label }}{% endtrans %}
  • +
  • {% trans %}Activer le matériel dans le navigateur{% endtrans %}
  • +
  • {% trans %}Gérer les utilisateurs{% endtrans %}
  • +
  • {% trans %}Gérer les documents{% endtrans %}
  • +
  • {% trans %}Gestion des {{ private_meetings }}{% endtrans %}
  • +
  • {% trans %}Déléguer la création de {{ meeting_label }}{% endtrans %}
  • +
  • {% trans %}Enregistrer {{ a_meeting }}{% endtrans %}
  • +
  • {% trans %}Créer un sondage{% endtrans %}
  • +
  • {% trans %}Joindre {{ a_meeting }} par téléphone{% endtrans %}
  • +
  • {% trans %}Afficher le tableau de bord{% endtrans %}
  • +
  • {% trans %}Gérer la mise en page{% endtrans %}
  • +
  • {% trans %}Contrôle du volume{% endtrans %}

  • +
    +
  • {% trans %}Créer {{ an_improvised_meeting }}{% endtrans %}
  • +
  • {% trans %}Participer à {{ a_meeting }}{% endtrans %}
  • +
  • {% trans %}Créer et paramétrer une salle de {{ meeting_label }}{% endtrans %}
  • +
  • {% trans %}Activer le matériel dans le navigateur{% endtrans %}
  • +
  • {% trans %}Gérer les utilisateurs{% endtrans %}
  • +
  • {% trans %}Gérer les documents{% endtrans %}
  • +
  • {% trans %}Gestion des {{ private_meetings }}{% endtrans %}
  • +
  • {% trans %}Déléguer la création de {{ meeting_label }}{% endtrans %}
  • +
  • {% trans %}Enregistrer {{ a_meeting }}{% endtrans %}
  • +
  • {% trans %}Créer un sondage{% endtrans %}
  • +
  • {% trans %}Joindre {{ a_meeting }} par téléphone{% endtrans %}
  • +
  • {% trans %}Afficher le tableau de bord{% endtrans %}
  • +
  • {% trans %}Gérer la mise en page{% endtrans %}
  • +
  • {% trans %}Contrôle du volume{% endtrans %}
  • +
    + - - - - - - - - - - - - - - + + + + + + + + + + + + + +
    1 caméra5 caméras10 caméras20 caméras30 caméras
    350 personnes250 personnes150 personnes100 personnesmaximum 50 personnes
    1 caméra5 caméras10 caméras20 caméras30 caméras
    350 personnes250 personnes150 personnes100 personnesmaximum 50 personnes
    -

    Documentation technique

    +

    Documentation technique

    -

    L'ensemble de ces services requiére d'ouverture de ports UDP notamment dans les pare-feux

    +

    L'ensemble de ces services requiére d'ouverture de ports UDP notamment dans les pare-feux

    Le service BigBlueButton est composé de 3 types de services : -

    Ces ports sont : -


    @@ -82,26 +82,26 @@

    Serveurs TURN

  • Instance Éducation, depuis la version 2.6 de BigBlueButton le service TURN est embarqué directement dans chaque serveur BigBlueButton et non plus une grappe de serveurs TURN dédiés.
  • Instance Webinaire de l'État, il y une grappe de serveurs TURN dédiés.
  • - - - - + + + + - - - - - + + + + + - +
    IPFQDNDomaine
    IPFQDNDomaine
    212.47.226.44bbb-dinum-turn1.visio.education.frWebinaire de l'état
    212.47.243.166bbb-dinum-turn2.visio.education.frWebinaire de l'état
    212.47.231.140bbb-dinum-turn3.visio.education.frWebinaire de l'état
    212.47.243.234bbb-dinum-turn4.visio.education.frWebinaire de l'état
    212.47.231.160bbb-dinum-turn5.visio.education.frWebinaire de l'état
    212.47.226.44bbb-dinum-turn1.visio.education.frWebinaire de l'état
    212.47.243.166bbb-dinum-turn2.visio.education.frWebinaire de l'état
    212.47.231.140bbb-dinum-turn3.visio.education.frWebinaire de l'état
    212.47.243.234bbb-dinum-turn4.visio.education.frWebinaire de l'état
    212.47.231.160bbb-dinum-turn5.visio.education.frWebinaire de l'état

    Fichiers avec les IP et FQDN des serveurs BBB

    {% endblock %} diff --git a/web/b3desk/templates/footer/donnees_personnelles.html b/web/b3desk/templates/footer/donnees_personnelles.html index 902f6bb6..df135e1f 100644 --- a/web/b3desk/templates/footer/donnees_personnelles.html +++ b/web/b3desk/templates/footer/donnees_personnelles.html @@ -1,100 +1,100 @@ {% extends 'static-layout.html' %} {% block main %} -
    -

    {% trans %}Données personnelles{% endtrans %}

    +
    +

    {% trans %}Données personnelles{% endtrans %}

    -

    {% trans %}Gestion des cookies{% endtrans %}

    -

    {% trans %}À propos des cookies / traceurs{% endtrans %}

    -

    {% trans %}Afin de permettre aux applications Web de fournir des services, des « cookies » (« traceurs » en français) peuvent être utilisés.{% endtrans %}

    +

    {% trans %}Gestion des cookies{% endtrans %}

    +

    {% trans %}À propos des cookies / traceurs{% endtrans %}

    +

    {% trans %}Afin de permettre aux applications Web de fournir des services, des « cookies » (« traceurs » en français) peuvent être utilisés.{% endtrans %}

    -

    {% trans %}Qu’est-ce qu’un « cookie » ?{% endtrans %}

    -

    {% trans %}Un « cookie » est un fichier de taille limitée, généralement constitué de lettres et de chiffres, envoyé par le serveur internet au fichier cookie du navigateur situé sur le disque dur de votre ordinateur.{% endtrans %}

    +

    {% trans %}Qu’est-ce qu’un « cookie » ?{% endtrans %}

    +

    {% trans %}Un « cookie » est un fichier de taille limitée, généralement constitué de lettres et de chiffres, envoyé par le serveur internet au fichier cookie du navigateur situé sur le disque dur de votre ordinateur.{% endtrans %}

    -

    {% trans %}Quels types de « cookies » sont utilisés par la le Webinaire de de l’Etat ?{% endtrans %}

    -

    {% trans %}Les cookies servent à l’authentification sur le site Webinaire/Visio-ecoles/colleges/lycees/agents{% endtrans %}

    -

    {% trans %}Il n’y a pas de cookie de mesure d’audience{% endtrans %}

    -

    {% trans %}Informations complémentaires sur les « cookies »{% endtrans %}

    -

    http://www.cnil.fr/vos-droits/vos-traces/les-cookies/

    +

    {% trans %}Quels types de « cookies » sont utilisés par la le Webinaire de de l’Etat ?{% endtrans %}

    +

    {% trans %}Les cookies servent à l’authentification sur le site Webinaire/Visio-ecoles/colleges/lycees/agents{% endtrans %}

    +

    {% trans %}Il n’y a pas de cookie de mesure d’audience{% endtrans %}

    +

    {% trans %}Informations complémentaires sur les « cookies »{% endtrans %}

    +

    http://www.cnil.fr/vos-droits/vos-traces/les-cookies/

    -

    {% trans %}Quelle sécurité pour vos données personnelles ?{% endtrans %}

    -

    {% trans %}Nous veillons à prendre les mesures physiques, techniques et organisationnelles appropriées pour garantir la sécurité et la confidentialité de vos données personnelles.{% endtrans %}

    +

    {% trans %}Quelle sécurité pour vos données personnelles ?{% endtrans %}

    +

    {% trans %}Nous veillons à prendre les mesures physiques, techniques et organisationnelles appropriées pour garantir la sécurité et la confidentialité de vos données personnelles.{% endtrans %}

    -

    {% trans %}Politique de confidentialité{% endtrans %}

    -

    {% trans %}Dans le cadre de l’utilisation du Webinaire, des données à caractère personnel vous concernant font l’objet d’un traitement par la DINUM (Direction Interministérielle du Numérique, ci-après « Nous »).{% endtrans %}

    -

    {% trans %}La présente politique décrit nos engagements en ce qui concerne le traitement de vos données, conformément au Règlement européen sur la protection des données (Règlement UE n° 2026/679 du 27 avril 2016, ci-après « RGPD ») et à la loi n° 78-17 du 6 janvier 1978 modifiée relative à l’informatique, aux fichiers et aux libertés (ci-après loi « Informatique et Libertés »).{% endtrans %}

    +

    {% trans %}Politique de confidentialité{% endtrans %}

    +

    {% trans %}Dans le cadre de l’utilisation du Webinaire, des données à caractère personnel vous concernant font l’objet d’un traitement par la DINUM (Direction Interministérielle du Numérique, ci-après « Nous »).{% endtrans %}

    +

    {% trans %}La présente politique décrit nos engagements en ce qui concerne le traitement de vos données, conformément au Règlement européen sur la protection des données (Règlement UE n° 2026/679 du 27 avril 2016, ci-après « RGPD ») et à la loi n° 78-17 du 6 janvier 1978 modifiée relative à l’informatique, aux fichiers et aux libertés (ci-après loi « Informatique et Libertés »).{% endtrans %}

    -

    {% trans %}Quel est notre rôle ?{% endtrans %}

    -

    {% trans %}Le Webinaire est un service en ligne proposé aux agents de l’Etat destiné à faciliter le travail à distance par l’organisation et la tenue de réunions, de conférences et de formations en ligne.{% endtrans %}

    -

    {% trans %}La DINUM est responsable de traitement des informations traitées dans le cadre du Service et, à ce titre, s’engage à respecter les obligations inhérentes à ce traitement, notamment celles relevant de la loi n° 78-17 du 6 janvier 1978 relative à l’informatique aux fichiers et aux libertés.{% endtrans %}

    -

    {% trans %}Les organisateurs qui procèdent aux invitations et à l’enregistrement des webinaires sont responsables des traitements ultérieurs qu’ils réalisent à partir de ces enregistrements. Il leur appartient de vous délivrer une information sur ces traitements.{% endtrans %}

    +

    {% trans %}Quel est notre rôle ?{% endtrans %}

    +

    {% trans %}Le Webinaire est un service en ligne proposé aux agents de l’Etat destiné à faciliter le travail à distance par l’organisation et la tenue de réunions, de conférences et de formations en ligne.{% endtrans %}

    +

    {% trans %}La DINUM est responsable de traitement des informations traitées dans le cadre du Service et, à ce titre, s’engage à respecter les obligations inhérentes à ce traitement, notamment celles relevant de la loi n° 78-17 du 6 janvier 1978 relative à l’informatique aux fichiers et aux libertés.{% endtrans %}

    +

    {% trans %}Les organisateurs qui procèdent aux invitations et à l’enregistrement des webinaires sont responsables des traitements ultérieurs qu’ils réalisent à partir de ces enregistrements. Il leur appartient de vous délivrer une information sur ces traitements.{% endtrans %}

    -

    {% trans %}Quels sont les traitements de données mis en œuvre ?{% endtrans %}

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    {% trans %}Finalité du traitement{% endtrans %}{% trans %}Base légale du traitement{% endtrans %}{% trans %}Catégorie de données personnelles traitées{% endtrans %}{% trans %}Durée de conservation{% endtrans %}
    {% trans %}Gestion des comptes des organisateurs de webinaires{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Données d’identification :{% endtrans %} - • {% trans %}Nom{% endtrans %} - • {% trans %}Prénom{% endtrans %} - • {% trans %}Adresse électronique{% endtrans %}{% trans %}Les données sont supprimées après une période de 12 mois d’inactivité du compte ou au moment de la désactivation du compte (1ère moe planifiée au plus tard en 12/22).{% endtrans %}
    {% trans %}Enregistrement des webinaires à des fins de partage et de rediffusion{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Enregistrement vidéo des webinaires{% endtrans %}{% trans %}Les données d’enregistrement peuvent être supprimées à tout moment par l’organisateur. Elles sont automatiquement supprimées 12 mois après la date du webinaire (1ère moe planifiée au plus tard en 12/22).{% endtrans %}
    {% trans %}Outils de chat et de vote en ligne{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Échanges écrits entre participants et résultats des votes en lignes{% endtrans %}{% trans %}Les données sont supprimées à l’issue de la session du webinaire.{% endtrans %}
    {% trans %}Gestion de la sécurité du Webinaire{% endtrans %}{% trans %}(Loi n° 2004-575 du 25 juin 2005 pour la confiance dans l’économie numérique, Décret n° 2021-1362 du 20 octobre 2021 relatif à la conservation des données permettant d’identifier toute personne ayant contribué à la création d’un contenu en ligne){% endtrans %}{% trans %}Données de connexion des utilisateurs du service (logs, adresse IP){% endtrans %}{% trans %}12 mois{% endtrans %}
    +

    {% trans %}Quels sont les traitements de données mis en œuvre ?{% endtrans %}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {% trans %}Finalité du traitement{% endtrans %}{% trans %}Base légale du traitement{% endtrans %}{% trans %}Catégorie de données personnelles traitées{% endtrans %}{% trans %}Durée de conservation{% endtrans %}
    {% trans %}Gestion des comptes des organisateurs de webinaires{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Données d’identification :{% endtrans %} + • {% trans %}Nom{% endtrans %} + • {% trans %}Prénom{% endtrans %} + • {% trans %}Adresse électronique{% endtrans %}{% trans %}Les données sont supprimées après une période de 12 mois d’inactivité du compte ou au moment de la désactivation du compte (1ère moe planifiée au plus tard en 12/22).{% endtrans %}
    {% trans %}Enregistrement des webinaires à des fins de partage et de rediffusion{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Enregistrement vidéo des webinaires{% endtrans %}{% trans %}Les données d’enregistrement peuvent être supprimées à tout moment par l’organisateur. Elles sont automatiquement supprimées 12 mois après la date du webinaire (1ère moe planifiée au plus tard en 12/22).{% endtrans %}
    {% trans %}Outils de chat et de vote en ligne{% endtrans %}{% trans %}Article 6-1(e) RGPD : exécution d’une mission de service public{% endtrans %}{% trans %}Échanges écrits entre participants et résultats des votes en lignes{% endtrans %}{% trans %}Les données sont supprimées à l’issue de la session du webinaire.{% endtrans %}
    {% trans %}Gestion de la sécurité du Webinaire{% endtrans %}{% trans %}(Loi n° 2004-575 du 25 juin 2005 pour la confiance dans l’économie numérique, Décret n° 2021-1362 du 20 octobre 2021 relatif à la conservation des données permettant d’identifier toute personne ayant contribué à la création d’un contenu en ligne){% endtrans %}{% trans %}Données de connexion des utilisateurs du service (logs, adresse IP){% endtrans %}{% trans %}12 mois{% endtrans %}
    -

    {% trans %}Quels sont les destinataires de vos données ?{% endtrans %}

    -

    {% trans %}Les destinataires de vos données personnelles sont les agents et salariées dûment habilitées de la DINUM et de nos sous-traitants qui interviennent pour l’hébergement, et l’infogérance du service.{% endtrans %}

    -

    {% trans %}Nous nous sommes assurés de la mise en œuvre par nos sous-traitants de garanties adéquates et du respect de conditions strictes de confidentialité, d’usage et de protection des données.{% endtrans %}

    -

    {% trans %}Les données collectées sur le Webinaire ne sont pas transférées en dehors de l’Union européenne.{% endtrans %}

    -

    {% trans %}Quels sont vos droits ?{% endtrans %}

    -

    {% trans %}Vous disposez d’un droit d’accès et de rectification des données à caractère personnel qui vous concernent. Vous disposez également d’un droit d’opposition et de limitation du traitement de vos données.{% endtrans %}

    -

    {% trans %}Pour exercer vos droits ou pour toute question sur le traitement de vos données, vous pouvez nous écrire à l’adresse : contact@webinaire.numerique.gouv.fr.{% endtrans %}

    -

    {% trans %}Vous pouvez également contacter le délégué à la protection des données (DPD) des services du Premier Ministre :{% endtrans %}

    +

    {% trans %}Quels sont les destinataires de vos données ?{% endtrans %}

    +

    {% trans %}Les destinataires de vos données personnelles sont les agents et salariées dûment habilitées de la DINUM et de nos sous-traitants qui interviennent pour l’hébergement, et l’infogérance du service.{% endtrans %}

    +

    {% trans %}Nous nous sommes assurés de la mise en œuvre par nos sous-traitants de garanties adéquates et du respect de conditions strictes de confidentialité, d’usage et de protection des données.{% endtrans %}

    +

    {% trans %}Les données collectées sur le Webinaire ne sont pas transférées en dehors de l’Union européenne.{% endtrans %}

    +

    {% trans %}Quels sont vos droits ?{% endtrans %}

    +

    {% trans %}Vous disposez d’un droit d’accès et de rectification des données à caractère personnel qui vous concernent. Vous disposez également d’un droit d’opposition et de limitation du traitement de vos données.{% endtrans %}

    +

    {% trans %}Pour exercer vos droits ou pour toute question sur le traitement de vos données, vous pouvez nous écrire à l’adresse : contact@webinaire.numerique.gouv.fr.{% endtrans %}

    +

    {% trans %}Vous pouvez également contacter le délégué à la protection des données (DPD) des services du Premier Ministre :{% endtrans %}

    • {% trans %}par mail à dpd@pm.gouv.fr{% endtrans %}
    • {% trans %}ou par courrier à l’adresse suivante :{% endtrans %}
    -

    {% trans %}Services du Premier Ministre{% endtrans %}

    -{% trans %}À l’attention du délégué à la protection des données (DPD)
    -56 rue de Varenne
    -75007 Paris{% endtrans %}
    -

    {% trans %}Si vous estimez, après nous avoir contactés, que vos droits Informatiques et Libertés ne sont pas respectés vous pouvez adresser une réclamation à la CNIL :{% endtrans %}

    -{% trans %}Commission nationale informatique et libertés
    -3 place de Fontenoy – TSA 80715 –
    -75334 PARIS CEDEX 07{% endtrans %}
    +

    {% trans %}Services du Premier Ministre{% endtrans %}

    + {% trans %}À l’attention du délégué à la protection des données (DPD)
    + 56 rue de Varenne
    + 75007 Paris{% endtrans %}
    +

    {% trans %}Si vous estimez, après nous avoir contactés, que vos droits Informatiques et Libertés ne sont pas respectés vous pouvez adresser une réclamation à la CNIL :{% endtrans %}

    + {% trans %}Commission nationale informatique et libertés
    + 3 place de Fontenoy – TSA 80715 –
    + 75334 PARIS CEDEX 07{% endtrans %}
    -

    {% trans %}Les modalités de réclamation sont précisées sur le site de la CNIL : www.cnil.fr.{% endtrans %}

    +

    {% trans %}Les modalités de réclamation sont précisées sur le site de la CNIL : www.cnil.fr.{% endtrans %}

    -

    {% trans %}Contactez le Délégué à la protection des données pour les services Écoles/Colléges/Lycées/Agents{% endtrans %}

    -

    {% trans %}Pour ce faire, envoyez un courriel à dpd[chez]education.gouv.fr{% endtrans %}

    -
    +

    {% trans %}Contactez le Délégué à la protection des données pour les services Écoles/Colléges/Lycées/Agents{% endtrans %}

    +

    {% trans %}Pour ce faire, envoyez un courriel à dpd[chez]education.gouv.fr{% endtrans %}

    +
    {% endblock %} diff --git a/web/b3desk/templates/footer/mentions_legales.html b/web/b3desk/templates/footer/mentions_legales.html index bf019ac6..1a92098d 100644 --- a/web/b3desk/templates/footer/mentions_legales.html +++ b/web/b3desk/templates/footer/mentions_legales.html @@ -1,7 +1,7 @@ {% extends 'static-layout.html' %} {% block main %} -
    +

    {% trans %}Mentions légales{% endtrans %}

    {% trans %}Éditeur{% endtrans %}

    {% trans %}Ministère de l’Éducation nationale et de la jeunesse Direction du numérique pour l’éducation 110 rue Grenelle, 75007 Paris{% endtrans %}
    {% trans %}Directeur de publication{% endtrans %}

    diff --git a/web/b3desk/templates/index.html b/web/b3desk/templates/index.html index 3930f6bd..d065cc2e 100644 --- a/web/b3desk/templates/index.html +++ b/web/b3desk/templates/index.html @@ -1,5 +1,5 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'jumbotron.html' %} +{% include 'jumbotron.html' %} {% endblock %} diff --git a/web/b3desk/templates/jumbotron.html b/web/b3desk/templates/jumbotron.html index c522dff7..cab9eb59 100644 --- a/web/b3desk/templates/jumbotron.html +++ b/web/b3desk/templates/jumbotron.html @@ -1,107 +1,107 @@ {% include 'rie.html' %} {% if user %}
    -
    - {% if stats is defined and stats %} -
    -
    -

    - - {% trans trimmed count=stats["runningCount"]%} - Actuellement, il y a {{ count }} webinaire - {% pluralize %} - Actuellement, il y a {{ count }} webinaires - {% endtrans %} - {% trans trimmed count=stats["participantCount"]%} - et {{ count }} participant - {% pluralize %} - et {{ count }} participants - {% endtrans %} - {% trans %} sur une capacité moyenne pour la plateforme de {{ max_participants }} participants.{% endtrans %} - -

    -
    +
    + {% if stats is defined and stats %} +
    +
    +

    + + {% trans trimmed count=stats["runningCount"]%} + Actuellement, il y a {{ count }} webinaire + {% pluralize %} + Actuellement, il y a {{ count }} webinaires + {% endtrans %} + {% trans trimmed count=stats["participantCount"]%} + et {{ count }} participant + {% pluralize %} + et {{ count }} participants + {% endtrans %} + {% trans %} sur une capacité moyenne pour la plateforme de {{ max_participants }} participants.{% endtrans %} + +

    +
    +
    + {% endif %}
    - {% endif %} -
    {% else %} -
    +
    -
    -
    -

    {% trans %}Vous organisez régulièrement des {{ some_meetings }}{% endtrans %}

    -

    {% trans %}Vous êtes agent de l’État, créez un compte pour organiser et conserver vos {{ some_meetings }}.{% endtrans %}

    -

    {% trans %}Se connecter ou créer un compte{% endtrans %}

    -

    - {% if stats is defined and stats %} -

    - - {% trans trimmed count=stats["runningCount"]%} - Actuellement, il y a {{ count }} webinaire - {% pluralize %} - Actuellement, il y a {{ count }} webinaires - {% endtrans %} - {% trans trimmed count=stats["participantCount"]%} - et {{ count }} participant - {% pluralize %} - et {{ count }} participants - {% endtrans %} - {% trans %} sur une capacité moyenne pour la plateforme de {{ max_participants }} participants.{% endtrans %} - -

    - {% endif %} -
    -
    +
    +
    +

    {% trans %}Vous organisez régulièrement des {{ some_meetings }}{% endtrans %}

    +

    {% trans %}Vous êtes agent de l’État, créez un compte pour organiser et conserver vos {{ some_meetings }}.{% endtrans %}

    +

    {% trans %}Se connecter ou créer un compte{% endtrans %}

    +

    + {% if stats is defined and stats %} +

    + + {% trans trimmed count=stats["runningCount"]%} + Actuellement, il y a {{ count }} webinaire + {% pluralize %} + Actuellement, il y a {{ count }} webinaires + {% endtrans %} + {% trans trimmed count=stats["participantCount"]%} + et {{ count }} participant + {% pluralize %} + et {{ count }} participants + {% endtrans %} + {% trans %} sur une capacité moyenne pour la plateforme de {{ max_participants }} participants.{% endtrans %} + +

    + {% endif %} +
    +
    +
    -
    -
    - {% if mail_meeting %} -
    -
    +
    +{% if mail_meeting %} +
    +
    -

    {% trans %}Démarrer {{ a_meeting }} en ligne immédiatement{% endtrans %}

    - {% with messages = get_flashed_messages(with_categories=true, category_filter=["error_login","success_login", "warning"]) %} +

    {% trans %}Démarrer {{ a_meeting }} en ligne immédiatement{% endtrans %}

    + {% with messages = get_flashed_messages(with_categories=true, category_filter=["error_login","success_login", "warning"]) %} {% if messages %} - {% for category,message in messages %} -
    -

    {{ message }}

    -
    - {% endfor %} - {% endif %} - {% endwith %} -

    {% trans %}Recevez par courriel un lien organisateur {{ of_the_meeting }}, actif une semaine, à envoyer aux participants.{% endtrans %}

    -
    - {% include 'meeting/csrf.html' %} -
    -
    - - -
    -
    - -
    + {% for category,message in messages %} +
    +

    {{ message }}

    - + {% endfor %} + {% endif %} + {% endwith %} +

    {% trans %}Recevez par courriel un lien organisateur {{ of_the_meeting }}, actif une semaine, à envoyer aux participants.{% endtrans %}

    +
    + {% include 'meeting/csrf.html' %} +
    +
    + + +
    +
    + +
    +
    +
    -
    - {% endif %} -
    -
    - {% if mail_meeting %} +
    +{% endif %} +
    +
    + {% if mail_meeting %} -
    -
    -

    {% trans %}Vous essayez de rejoindre {{ a_meeting }}{% endtrans %}

    -

    {% trans %}Pour rejoindre {{ a_meeting_to_which }} vous êtes invité, cliquez sur le lien que vous a transmis l'organisateur/modérateur.{% endtrans %}

    -

    -
    +
    +
    +

    {% trans %}Vous essayez de rejoindre {{ a_meeting }}{% endtrans %}

    +

    {% trans %}Pour rejoindre {{ a_meeting_to_which }} vous êtes invité, cliquez sur le lien que vous a transmis l'organisateur/modérateur.{% endtrans %}

    +

    +
    +
    + {% endif %}
    - {% endif %} -
    -
    +
    {% endif %} diff --git a/web/b3desk/templates/layout.html b/web/b3desk/templates/layout.html index 635e6008..275323d1 100644 --- a/web/b3desk/templates/layout.html +++ b/web/b3desk/templates/layout.html @@ -1,85 +1,85 @@ - - - - - - + + + + + + - {{ config["TITLE"] }} + {{ config["TITLE"] }} - - - - - - - - - - + + + + + + + + + + - -
    - {% block header %} - {% include 'header.html' %} - {% endblock %} -
    - {% with messages = get_flashed_messages(with_categories=true, category_filter=["error","success", "message", "warning"]) %} - {% if messages %} - {% for category,message in messages %} -
    -

    {{ message }}

    -
    - {% endfor %} - {% endif %} - {% endwith %} - {% block jumbotron %}{% endblock %} -
    -
    -
    - {% block main %}{% endblock %} -
    -
    -
    -
    + +
    + {% block header %} + {% include 'header.html' %} + {% endblock %} +
    + {% with messages = get_flashed_messages(with_categories=true, category_filter=["error","success", "message", "warning"]) %} + {% if messages %} + {% for category,message in messages %} +
    +

    {{ message }}

    +
    + {% endfor %} + {% endif %} + {% endwith %} + {% block jumbotron %}{% endblock %} +
    +
    +
    + {% block main %}{% endblock %} +
    +
    +
    +
    - {% block footer %} - {% include 'footer.html' %} - {% endblock %} + {% block footer %} + {% include 'footer.html' %} + {% endblock %} - - - - - - {% if config.get("MATOMO_SITE_ID") and config.get("MATOMO_URL") %} - - {% endif %} - {% block js %}{% endblock %} -
    - + + + + + + {% if config.get("MATOMO_SITE_ID") and config.get("MATOMO_URL") %} + + {% endif %} + {% block js %}{% endblock %} +
    + diff --git a/web/b3desk/templates/meeting/card.html b/web/b3desk/templates/meeting/card.html index b39e8966..5626c549 100644 --- a/web/b3desk/templates/meeting/card.html +++ b/web/b3desk/templates/meeting/card.html @@ -1,8 +1,8 @@
    -
    -

    {{ meeting.name }}

    - {% include 'meeting/row.html' %} -
    +
    +

    {{ meeting.name }}

    + {% include 'meeting/row.html' %} +
    {% include 'meeting/modals/delete.html' %} {% include 'meeting/modals/invite.html' %} diff --git a/web/b3desk/templates/meeting/edit.html b/web/b3desk/templates/meeting/edit.html index 827e2325..e3305cbf 100644 --- a/web/b3desk/templates/meeting/edit.html +++ b/web/b3desk/templates/meeting/edit.html @@ -1,9 +1,9 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'meeting/jumbotron.html' %} +{% include 'meeting/jumbotron.html' %} {% endblock %} {% block main %} - {% include 'meeting/form.html' %} +{% include 'meeting/form.html' %} {% endblock %} diff --git a/web/b3desk/templates/meeting/end.html b/web/b3desk/templates/meeting/end.html index 0891c9f7..4aa32d58 100644 --- a/web/b3desk/templates/meeting/end.html +++ b/web/b3desk/templates/meeting/end.html @@ -2,8 +2,8 @@ {% block main %}

    - {{ this_meeting.capitalize() }} est actuellement en cours avec les anciens paramètres.
    - Souhaitez-vous y mettre fin pour permettre la prise en compte immédiate de ces nouveaux paramètres ? Les éventuels participants seront déconnectés. +{{ this_meeting.capitalize() }} est actuellement en cours avec les anciens paramètres.
    +Souhaitez-vous y mettre fin pour permettre la prise en compte immédiate de ces nouveaux paramètres ? Les éventuels participants seront déconnectés.

    diff --git a/web/b3desk/templates/meeting/externalUpload.html b/web/b3desk/templates/meeting/externalUpload.html index 9941fe3c..4c102dc2 100644 --- a/web/b3desk/templates/meeting/externalUpload.html +++ b/web/b3desk/templates/meeting/externalUpload.html @@ -1,68 +1,67 @@ -
    - - -
    - - +
    diff --git a/web/b3desk/templates/meeting/files.html b/web/b3desk/templates/meeting/files.html index 213ba449..ca42ecc5 100644 --- a/web/b3desk/templates/meeting/files.html +++ b/web/b3desk/templates/meeting/files.html @@ -1,43 +1,42 @@ {% macro render_field(field, generate_button=False) %}
    - {% if field.type == "BooleanField" %} -
    - {{ field(value="on")}} - {{ field.label(class_="fr-label") }} -

    {{ field.description }}

    -
    - {% else %} -
    - {{ field.label(class_="fr-label") }} -

    {{ field.description }}

    - {% if field.errors %} - {% for error in field.errors %} -

    {{ error }}

    - {% endfor %} - {% endif %} - {% if generate_button %} -
    -
    - {{ field(class_="fr-input") }} -
    -
    - -
    + {% if field.type == "BooleanField" %} +
    + {{ field(value="on")}} + {{ field.label(class_="fr-label") }} +

    {{ field.description }}

    {% else %} - {{ field(class_="fr-input", **kwargs) }} +
    + {{ field.label(class_="fr-label") }} +

    {{ field.description }}

    + {% if field.errors %} + {% for error in field.errors %} +

    {{ error }}

    + {% endfor %} + {% endif %} + {% if generate_button %} +
    +
    + {{ field(class_="fr-input") }} +
    +
    + +
    +
    + {% else %} + {{ field(class_="fr-input", **kwargs) }} + {% endif %} +
    {% endif %} -
    - {% endif %}
    {% endmacro %} - + - +

    Gestion des {{ meeting_presentation }}s associées à {{ meeting.name }}

    @@ -46,178 +45,177 @@

    Gestion des {{ meeting_presentation }}s associées à {{ m

    -{% if form.errors %} -

    Enregistrement impossible car certains champs sont mal renseignés.

    -{% endif %} - + {% if form.errors %} +

    Enregistrement impossible car certains champs sont mal renseignés.

    + {% endif %}
    - -
    + +
    -
    -
    -
    - -
    -
    - - {% include 'meeting/csrf.html' %} - {% include 'meeting/id.html' %} -
    -
    - -
    +
    +
    +
    + +
    +
    +
    + {% include 'meeting/csrf.html' %} + {% include 'meeting/id.html' %} +
    +
    +
    +
    +
    -
    -
    -
    +
    +
    - -
    + +
    -
    -
    -
    - -
    -
    -
    - {% include 'meeting/csrf.html' %} - {% include 'meeting/id.html' %} -
    - {{ render_field(form.url) }} - -
    -
    +
    +
    +
    + +
    +
    +
    + {% include 'meeting/csrf.html' %} + {% include 'meeting/id.html' %} +
    + {{ render_field(form.url) }} + +
    +
    +
    -
    -
    -
    -
    +
    +
    +
    -

    Vous pouvez ajouter un fichier :

    -
    +

    Vous pouvez ajouter un fichier :

    +
    + +
      +
    • + +
    • + {% if user.nc_login and user.nc_token and user.nc_locator %} +
    • + +
    • +
    • + +
    • + {% endif %} +
    -
      -
    • - -
    • - {% if user.nc_login and user.nc_token and user.nc_locator %} -
    • - -
    • -
    • - -
    • - {% endif %} -
    - -
    +
    - +
    -
    - - - - - - - - - - - - - - {% for file in meeting.files %} - {% if (user.nc_login and user.nc_token and user.nc_locator) or file.url %} - - -
    -
    -
    -
    -
    - -
    -
    -

    - Supprimer -

    -

    Voulez-vous vraiment supprimer le fichier {{ file.short_title }} ?

    -
    - - - - {% include 'meeting/csrf.html' %} - +
    +
    Vos fichiers associés sont :
    DéfautTéléchargeableTitreAjouté leSupprimerTélécharger
    + + + + + + + + + + + + + {% for file in meeting.files %} + {% if (user.nc_login and user.nc_token and user.nc_locator) or file.url %} + + +
    +
    +
    +
    +
    + +
    +
    +

    + Supprimer +

    +

    Voulez-vous vraiment supprimer le fichier {{ file.short_title }} ?

    +
    + + + + {% include 'meeting/csrf.html' %} + +
    +
    +
    - - - -
    - - - - - - - - - + + + + + + + + + + + {% endif %} + {% endfor %} + +
    Vos fichiers associés sont :
    DéfautTéléchargeableTitreAjouté leSupprimerTélécharger
    -
    - {% if file.is_default %} - - - {% else %} - - - {% endif %} -
    -
    -
    - {% if file.is_downloadable %} - - - {% else %} - - - {% endif %} -
    -
    {{ file.short_title }}{{ file.created_at }} - - - - - -
    +
    + {% if file.is_default %} + + + {% else %} + + + {% endif %} +
    +
    +
    + {% if file.is_downloadable %} + + + {% else %} + + + {% endif %} +
    +
    {{ file.short_title }}{{ file.created_at }} + + + + + +
    +
    + {% if not meeting.files|length %} +

    Aucun document de présentation n'est prévu pour cette réunion actuellement

    {% endif %} - {% endfor %} - -
    - {% if not meeting.files|length %} -

    Aucun document de présentation n'est prévu pour cette réunion actuellement

    - {% endif %} -
    - diff --git a/web/b3desk/templates/meeting/filesform.html b/web/b3desk/templates/meeting/filesform.html index 724094db..cdb8bf92 100644 --- a/web/b3desk/templates/meeting/filesform.html +++ b/web/b3desk/templates/meeting/filesform.html @@ -1,7 +1,7 @@ {% extends 'layout.html' %} {% block main %} - {% include 'meeting/files.html' %} +{% include 'meeting/files.html' %} {% endblock %} {% block js %} diff --git a/web/b3desk/templates/meeting/id.html b/web/b3desk/templates/meeting/id.html index c68d69ab..f6f66ac2 100644 --- a/web/b3desk/templates/meeting/id.html +++ b/web/b3desk/templates/meeting/id.html @@ -1,3 +1,3 @@ {% if meeting is not none %} - + {% endif %} diff --git a/web/b3desk/templates/meeting/join.html b/web/b3desk/templates/meeting/join.html index 56041f38..61c3e275 100644 --- a/web/b3desk/templates/meeting/join.html +++ b/web/b3desk/templates/meeting/join.html @@ -1,42 +1,42 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'rie.html' %} +{% include 'rie.html' %} {% endblock %} {% block main %} -

    {% trans %}Rejoindre {{ the_meeting }}{% endtrans %}

    +

    {% trans %}Rejoindre {{ the_meeting }}{% endtrans %}

    -
    - {% include 'meeting/csrf.html' %} - - - + + {% include 'meeting/csrf.html' %} + + + -
    +
    -
    - - {% if role != "authenticated" %}

    {% trans %}Vous pouvez également préciser votre service ou votre fonction.{% endtrans %}

    {% endif %} - -
    +
    + + {% if role != "authenticated" %}

    {% trans %}Vous pouvez également préciser votre service ou votre fonction.{% endtrans %}

    {% endif %} + +
    {% if role == "authenticated" %}
    -
    - -

    Si ce champ est rempli, il sera affiché dans BBB.

    - -
    +
    + +

    Si ce champ est rempli, il sera affiché dans BBB.

    + +

    Le nom affiché sera «  ».

    {% endif %}
    -
    - -
    +
    + +
    {% if role != "authenticated" and not user %} @@ -49,24 +49,24 @@

    {% trans %}Rejoindre {{ the_meeting }}{% endtrans %}

    {% endif %} -
    - +
    + - {% if role == "authenticated" %} - - {% endif %} + } + updatePreview() + +{% endif %} {% endblock %} diff --git a/web/b3desk/templates/meeting/joinmail.html b/web/b3desk/templates/meeting/joinmail.html index 294ad9a3..95096a40 100644 --- a/web/b3desk/templates/meeting/joinmail.html +++ b/web/b3desk/templates/meeting/joinmail.html @@ -1,9 +1,9 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'rie.html' %} +{% include 'rie.html' %} {% endblock %} {% block main %} - {% include 'meeting/signinmail.html' %} +{% include 'meeting/signinmail.html' %} {% endblock %} diff --git a/web/b3desk/templates/meeting/jumbotron.html b/web/b3desk/templates/meeting/jumbotron.html index bd82f72c..0d567bfb 100644 --- a/web/b3desk/templates/meeting/jumbotron.html +++ b/web/b3desk/templates/meeting/jumbotron.html @@ -1,10 +1,10 @@
    - {% if meeting is not none %} + {% if meeting is not none %}

    {% trans %}Webinaire{% endtrans %} {{ meeting.name }}

    - {% else %} + {% else %}

    {% trans %}Nouveau webinaire{% endtrans %}

    - {% endif %} - - {% trans %}Retour au tableau de bord{% endtrans %} - + {% endif %} + + {% trans %}Retour au tableau de bord{% endtrans %} +
    diff --git a/web/b3desk/templates/meeting/list.html b/web/b3desk/templates/meeting/list.html index 22a8ace7..118475b4 100644 --- a/web/b3desk/templates/meeting/list.html +++ b/web/b3desk/templates/meeting/list.html @@ -1,57 +1,57 @@
    -
    -
    -

    {% trans %}Mes salles de {{ some_meetings }}{% endtrans %}

    - {% if can_create_meetings == true %} - - {% endif %} +
    +
    +

    {% trans %}Mes salles de {{ some_meetings }}{% endtrans %}

    + {% if can_create_meetings == true %} + + {% endif %} +
    -
    - {% if not can_create_meetings %} -
    -

    - - {% trans %}Vous avez atteint la limite des {{ max_meetings_per_user }} {{ meeting_label }}s. Pour pouvoir en créer davantage, veuillez supprimer des {{ meeting_label }}s inactives.{% endtrans %} -

    -
    - {% endif %} - - {% if not user.meetings %} -
    -
    + {% if not can_create_meetings %} +
    +

    + + {% trans %}Vous avez atteint la limite des {{ max_meetings_per_user }} {{ meeting_label }}s. Pour pouvoir en créer davantage, veuillez supprimer des {{ meeting_label }}s inactives.{% endtrans %} +

    -
    -

    Vous n'avez pas encore créé de salle de {{ some_meetings }} permanente. Vous pouvez créer votre salle permanente ou accéder à une salle temporaire plus bas.

    - - Créer une salle permanente - -

    - En créant une salle de réunion permanente, vous pouvez créer une base de documents permanente, choisir d'ajouter une salle d'attente, de bloquer les micros et les caméras ouverts ou fermés, d'afficher ou non les espaces de discussions privés et publiques, ou encore accéder aux enregistrements à la fin de votre réunion. -

    + {% endif %} + + {% if not user.meetings %} +
    +
    +
    +
    +

    Vous n'avez pas encore créé de salle de {{ some_meetings }} permanente. Vous pouvez créer votre salle permanente ou accéder à une salle temporaire plus bas.

    + + Créer une salle permanente + +

    + En créant une salle de réunion permanente, vous pouvez créer une base de documents permanente, choisir d'ajouter une salle d'attente, de bloquer les micros et les caméras ouverts ou fermés, d'afficher ou non les espaces de discussions privés et publiques, ou encore accéder aux enregistrements à la fin de votre réunion. +

    +
    -
    - {% else %} + {% else %} {% for meeting in user.meetings %}
    -
    -

    {{ meeting.name }}

    - {% include 'meeting/row.html' %} -
    +
    +

    {{ meeting.name }}

    + {% include 'meeting/row.html' %} +
    {% include 'meeting/modals/delete.html' %} {% include 'meeting/modals/invite.html' %} {% include 'meeting/modals/recordings.html' %} {% endfor %} {% if user.meetings|length and config.get("SATISFACTION_POLL_URL") %} - {% endif %} -
    +
    {% endif %} diff --git a/web/b3desk/templates/meeting/mailtest.html b/web/b3desk/templates/meeting/mailtest.html index 0e429bf4..08950ee0 100644 --- a/web/b3desk/templates/meeting/mailtest.html +++ b/web/b3desk/templates/meeting/mailtest.html @@ -1,3 +1,3 @@

    {{ url }}

    -
    +
    diff --git a/web/b3desk/templates/meeting/modals/delete.html b/web/b3desk/templates/meeting/modals/delete.html index fd678e8c..ba61df09 100644 --- a/web/b3desk/templates/meeting/modals/delete.html +++ b/web/b3desk/templates/meeting/modals/delete.html @@ -1,18 +1,18 @@ diff --git a/web/b3desk/templates/meeting/modals/invite.html b/web/b3desk/templates/meeting/modals/invite.html index b7d15314..f164a62a 100644 --- a/web/b3desk/templates/meeting/modals/invite.html +++ b/web/b3desk/templates/meeting/modals/invite.html @@ -4,49 +4,49 @@

    Inviter les modérateurs

    En leur envoyant l’adresse ci-dessous :

    -
    - {{ meeting.get_signin_url("moderator") }} -
    -
    - -
    +
    + {{ meeting.get_signin_url("moderator") }} +
    +
    + +

    - Rejoindre {{ the_meeting }} en tant que modérateur principal + Rejoindre {{ the_meeting }} en tant que modérateur principal

    Inviter les participants

    En leur envoyant l’adresse ci-dessous :

    -
    - {{ meeting.get_signin_url("attendee") }} -
    -
    - -
    +
    + {{ meeting.get_signin_url("attendee") }} +
    +
    + +

    - Rejoindre {{ the_meeting }} en tant que participant + Rejoindre {{ the_meeting }} en tant que participant

    Inviter les participants authentifiés {% if config.OIDC_ATTENDEE_SERVICE_NAME %}via {{ config.OIDC_ATTENDEE_SERVICE_NAME }} {% endif %}

    En leur envoyant l’adresse ci-dessous :

    -
    - {{ meeting.get_signin_url("authenticated") }} -
    -
    - -
    +
    + {{ meeting.get_signin_url("authenticated") }} +
    +
    + +

    - Rejoindre {{ the_meeting }} en tant que participant authentifié + Rejoindre {{ the_meeting }} en tant que participant authentifié

    diff --git a/web/b3desk/templates/meeting/modals/recordings.html b/web/b3desk/templates/meeting/modals/recordings.html index f63aa3f9..f8426236 100644 --- a/web/b3desk/templates/meeting/modals/recordings.html +++ b/web/b3desk/templates/meeting/modals/recordings.html @@ -1,54 +1,54 @@
    diff --git a/web/b3desk/templates/meeting/recordings.html b/web/b3desk/templates/meeting/recordings.html index 8d67f4b4..0469340b 100644 --- a/web/b3desk/templates/meeting/recordings.html +++ b/web/b3desk/templates/meeting/recordings.html @@ -10,10 +10,10 @@

    {% trans meeting_name=meeting.name %}Enregistrements de {{
    {% if meeting.is_running() %} -

    - {% trans this_meeting=this_meeting.capitalize() %}{{ this_meeting }} est toujours en cours{% endtrans %}. - {% trans %}Si un enregistrement est en cours, il ne sera encodé qu'après la fin {{ of_the_meeting }}{% endtrans %}. -

    +

    + {% trans this_meeting=this_meeting.capitalize() %}{{ this_meeting }} est toujours en cours{% endtrans %}. + {% trans %}Si un enregistrement est en cours, il ne sera encodé qu'après la fin {{ of_the_meeting }}{% endtrans %}. +

    {% endif %}

    {% trans %}Après la fin d'{{ a_meeting }}, l'encodage de l'enregistrement peut prendre autant de temps que la durée {{ of_the_meeting }}.{% endtrans %}

    {% trans %}Si aucun modérateur ne met fin {{ of_the_meeting }}, un délai supplémentaire de plusieurs minutes s'ajoute après que tous les utilisateurs l'aient quitté.{% endtrans %}

    @@ -22,59 +22,59 @@

    {% trans meeting_name=meeting.name %}Enregistrements de {{

    {{ recording_name }}

    - - - - - - {% set p = r.playbacks.get("presentation") %} - {% if p %} - - + + + + + {% set p = r.playbacks.get("presentation") %} + {% if p %} + + + + {% endif %} - {% endfor %} - - - - - {% endif %} - +
    {% trans %}Visuels{% endtrans %}{% trans %}Actions{% endtrans %}
    -
    - {% for i in p.images %} - {% if loop.index is le(2) %} -
    - {{ i.alt }} -
    +
    {% trans %}Visuels{% endtrans %}{% trans %}Actions{% endtrans %}
    +
    + {% for i in p.images %} + {% if loop.index is le(2) %} +
    + {{ i.alt }} +
    + {% endif %} + {% endfor %} +
    +
    +
    + + + {% if r.playbacks.get("video") %} + mp4 + {% endif %} + +
    +
    -
    - - - {% if r.playbacks.get("video") %} - mp4 - {% endif %} - -
    -
    @@ -83,11 +83,11 @@

    @@ -101,9 +101,9 @@

    {{ form.name.label(class_="fr-label") }}

    {{ form.name.description }}

    {% if form.name.errors %} - {% for error in form.name.errors %} -

    {{ error }}

    - {% endfor %} + {% for error in form.name.errors %} +

    {{ error }}

    + {% endfor %} {% endif %} {{ form.name(value=recording_name, class_="fr-input") }} @@ -116,8 +116,8 @@

    {% endfor %} - - + + {% endblock %} diff --git a/web/b3desk/templates/meeting/row.html b/web/b3desk/templates/meeting/row.html index d2e904ad..d5425ebd 100644 --- a/web/b3desk/templates/meeting/row.html +++ b/web/b3desk/templates/meeting/row.html @@ -1,66 +1,66 @@
    - {% if config.get("SHORTY") %} -
    - - - {% if mailto %} - - {% endif %} - -
    -
    - - - {% if mailto %} - - {% endif %} - -
    -
    + {% if config.get("SHORTY") %} +
    + + + {% if mailto %} + + {% endif %} + +
    +
    + + + {% if mailto %} + + {% endif %} + +
    +
    - {% if config.get("FILE_SHARING") %} + {% if config.get("FILE_SHARING") %} - {% endif %} - -
    - {% else %} -
    - {% trans %}Lancer{% endtrans %} - -
    -
    - {% if config.get("RECORDING") %} + {% endif %} + +
    + {% else %} +
    + {% trans %}Lancer{% endtrans %} + +
    +
    + {% if config.get("RECORDING") %} - + - {% endif %} - - {% if config.get("FILE_SHARING") %} + {% endif %} + + {% if config.get("FILE_SHARING") %} - {% endif %} - -
    - {% endif %} + {% endif %} + +
    + {% endif %}
    diff --git a/web/b3desk/templates/meeting/show.html b/web/b3desk/templates/meeting/show.html index 7d71d8e5..00690947 100644 --- a/web/b3desk/templates/meeting/show.html +++ b/web/b3desk/templates/meeting/show.html @@ -1,9 +1,9 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'jumbotron.html' %} +{% include 'jumbotron.html' %} {% endblock %} {% block main %} - {% include 'meeting/card.html' %} +{% include 'meeting/card.html' %} {% endblock %} diff --git a/web/b3desk/templates/meeting/submit.html b/web/b3desk/templates/meeting/submit.html index c1b50bdb..85c4075c 100644 --- a/web/b3desk/templates/meeting/submit.html +++ b/web/b3desk/templates/meeting/submit.html @@ -1,8 +1,8 @@
    -{% if meeting is not none %} - -{% else %} - -{% endif %} -{% trans %}Annuler{% endtrans %} + {% if meeting is not none %} + + {% else %} + + {% endif %} + {% trans %}Annuler{% endtrans %}
    diff --git a/web/b3desk/templates/meeting/wizard.html b/web/b3desk/templates/meeting/wizard.html index ce0a17ca..df9391ed 100644 --- a/web/b3desk/templates/meeting/wizard.html +++ b/web/b3desk/templates/meeting/wizard.html @@ -4,5 +4,5 @@ {% endblock %} {% block main %} - {% include 'meeting/form.html' %} +{% include 'meeting/form.html' %} {% endblock %} diff --git a/web/b3desk/templates/redirections.html b/web/b3desk/templates/redirections.html index e799f9e3..9ee67870 100644 --- a/web/b3desk/templates/redirections.html +++ b/web/b3desk/templates/redirections.html @@ -1,13 +1,13 @@ -
    +

    {% trans %}Autres profils{% endtrans %}

    {% for link in redirection_links %} -

    {{ link.text }}

    +

    {{ link.text }}

    {% endfor %} -
    +
    diff --git a/web/b3desk/templates/static-layout.html b/web/b3desk/templates/static-layout.html index 73b67e30..87a135a8 100644 --- a/web/b3desk/templates/static-layout.html +++ b/web/b3desk/templates/static-layout.html @@ -1,62 +1,60 @@ - - - - - - + + + + + + - {{ config["TITLE"] }} + {{ config["TITLE"] }} - - - - - - - + + + + + + - -
    - {% block header %} - {% include 'header.html' %} - {% endblock %} -
    -
    -
    - {% block main %}{% endblock %} -
    -
    -
    + +
    + {% block header %} + {% include 'header.html' %} + {% endblock %} +
    +
    +
    + {% block main %}{% endblock %} +
    +
    +
    - {% block footer %} - {% include 'footer.html' %} - {% endblock %} + {% block footer %} + {% include 'footer.html' %} + {% endblock %} - - - - - - - - {% if config.get("MATOMO_SITE_ID") and config.get("MATOMO_URL") %} - - {% endif %} - {% block js %}{% endblock %} -
    - + + + + + + + {% if config.get("MATOMO_SITE_ID") and config.get("MATOMO_URL") %} + + {% endif %} + {% block js %}{% endblock %} +
    + diff --git a/web/b3desk/templates/tools.html b/web/b3desk/templates/tools.html index f0265aa2..6db2a127 100644 --- a/web/b3desk/templates/tools.html +++ b/web/b3desk/templates/tools.html @@ -1,46 +1,46 @@ diff --git a/web/b3desk/templates/welcome.html b/web/b3desk/templates/welcome.html index a426f7c7..07208ffb 100644 --- a/web/b3desk/templates/welcome.html +++ b/web/b3desk/templates/welcome.html @@ -1,9 +1,9 @@ {% extends 'layout.html' %} {% block jumbotron %} - {% include 'jumbotron.html' %} +{% include 'jumbotron.html' %} {% endblock %} {% block main %} - {% include 'page.html' %} +{% include 'page.html' %} {% endblock %} From ee8b52e633dccd053fe2ca70fdf38662b5cd56af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 23 Feb 2024 16:10:22 +0100 Subject: [PATCH 164/213] refactor: NC connection --- web/b3desk/models/users.py | 84 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/web/b3desk/models/users.py b/web/b3desk/models/users.py index 435ccb4b..968946f3 100644 --- a/web/b3desk/models/users.py +++ b/web/b3desk/models/users.py @@ -74,17 +74,47 @@ def get_user_nc_credentials(username): return result -def get_or_create_user(user_info): +def update_user_nc_credentials(user, user_info): # preferred_username is login from keycloak, REQUIRED for nc_login connexion # data is conveyed like following : # user logs in to keycloak # visio-agent retrives preferred_username from keycloack ( aka keycloak LOGIN, which is immutable ) # visio-agent calls EDNAT API for NC_DATA retrieval, passing LOGIN as postData # visio-agent can now connect to remote NC with NC_DATA - if current_app.config["FILE_SHARING"]: - preferred_username = user_info.get("preferred_username") + if ( + user.nc_last_auto_enroll + and user.nc_locator + and user.nc_token + and ( + (datetime.now() - user.nc_last_auto_enroll).days + <= current_app.config["NC_LOGIN_TIMEDELTA_DAYS"] + ) + ): + return False + + preferred_username = ( + user_info.get("preferred_username") + if current_app.config["FILE_SHARING"] + else None + ) + data = get_user_nc_credentials(preferred_username) + if ( + preferred_username is None + or data["nclocator"] is None + or data["nctoken"] is None + ): + nc_last_auto_enroll = None else: - preferred_username = None + nc_last_auto_enroll = datetime.now() + + user.nc_locator = data["nclocator"] + user.nc_token = data["nctoken"] + user.nc_login = preferred_username + user.nc_last_auto_enroll = nc_last_auto_enroll + return True + + +def get_or_create_user(user_info): given_name = user_info["given_name"] family_name = user_info["family_name"] email = user_info["email"].lower() @@ -92,68 +122,36 @@ def get_or_create_user(user_info): user = User.query.filter_by(email=email).first() if user is None: - data = get_user_nc_credentials(preferred_username) - nc_locator, nc_token, nc_login = ( - data["nclocator"], - data["nctoken"], - preferred_username, - ) - if nc_locator is None or nc_login is None or nc_token is None: - nc_last_auto_enroll = None - else: - nc_last_auto_enroll = datetime.now() user = User( email=email, given_name=given_name, family_name=family_name, - nc_locator=nc_locator, - nc_login=nc_login, - nc_token=nc_token, - nc_last_auto_enroll=nc_last_auto_enroll, last_connection_utc_datetime=datetime.utcnow(), ) + update_user_nc_credentials(user, user_info) user.save() + else: - user_has_changed = False - if ( - not user.nc_last_auto_enroll - or not user.nc_locator - or not user.nc_token - or ( - (datetime.now() - user.nc_last_auto_enroll).days - > current_app.config["NC_LOGIN_TIMEDELTA_DAYS"] - ) - ): - data = get_user_nc_credentials(preferred_username) - nc_locator, nc_token, nc_login = ( - data["nclocator"], - data["nctoken"], - preferred_username, - ) - if nc_locator is None or nc_login is None or nc_token is None: - nc_last_auto_enroll = None - else: - nc_last_auto_enroll = datetime.now() - user.nc_token = nc_token - user.nc_login = nc_login - user.nc_locator = nc_locator - user.nc_last_auto_enroll = nc_last_auto_enroll - user_has_changed = True + user_has_changed = update_user_nc_credentials(user, user_info) if user.given_name != given_name: user.given_name = given_name user_has_changed = True + if user.family_name != family_name: user.family_name = family_name user_has_changed = True + if ( not user.last_connection_utc_datetime or user.last_connection_utc_datetime.date() < date.today() ): user.last_connection_utc_datetime = datetime.utcnow() user_has_changed = True + if user_has_changed: user.save() + return user From a2f0dda00f6fb776f0ddc8e097ad7903502b09bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 23 Feb 2024 16:11:23 +0100 Subject: [PATCH 165/213] refactor: remove useless var --- web/b3desk/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index a979efff..91088da0 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -29,7 +29,6 @@ __version__ = "1.1.2" -CRITICAL_VARS = ["OIDC_ISSUER", "OIDC_CLIENT_SECRET", "BIGBLUEBUTTON_SECRET"] LANGUAGES = ["en", "fr"] babel = Babel() From bfd8149e4083e7fd7b14e8f209d61c8292b589e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 23 Feb 2024 17:15:30 +0100 Subject: [PATCH 166/213] doc: WTF_CSRF_TIME_LIMIT instructions --- web/b3desk/settings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index 8dcb3f02..7a632b55 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -193,9 +193,14 @@ class MainSettings(BaseSettings): EXTERNAL_UPLOAD_DESCRIPTION: str = "Fichiers depuis votre Nextcloud" """Description dans BBB des fichiers téléversés dans Nextcloud.""" - WTF_CSRF_TIME_LIMIT: int = 3600 * 12 + WTF_CSRF_TIME_LIMIT: int = 3600 * 24 """Indique en secondes la durée de validit des jetons CSRF. + Il est nécessaire de mettre une valeur plus élevée que le délai de + mise en cache des pages par le serveur web. Sans quoi les + navigateurs des utilisateurs serviront des pages en caches contenant + des jetons CSRF expirés. + Plus d'infos sur https://flask-wtf.readthedocs.io/en/1.2.x/config/ """ From 30fbaa0ffc7f7906f32908aba83b9e1e0a5ff2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 23 Feb 2024 17:26:12 +0100 Subject: [PATCH 167/213] doc: typo --- web/b3desk/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index 7a632b55..8774ec29 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -194,7 +194,7 @@ class MainSettings(BaseSettings): """Description dans BBB des fichiers téléversés dans Nextcloud.""" WTF_CSRF_TIME_LIMIT: int = 3600 * 24 - """Indique en secondes la durée de validit des jetons CSRF. + """Indique en secondes la durée de validité des jetons CSRF. Il est nécessaire de mettre une valeur plus élevée que le délai de mise en cache des pages par le serveur web. Sans quoi les From dcf99d49109017a98777fe937ed1651da3667c02 Mon Sep 17 00:00:00 2001 From: Loan Robert Date: Mon, 11 Mar 2024 17:39:56 +0100 Subject: [PATCH 168/213] refactor: API endpoint refactoring and testing --- web/b3desk/__init__.py | 2 + web/b3desk/endpoints/api.py | 28 ++++++++ web/b3desk/endpoints/meetings.py | 26 -------- web/b3desk/session.py | 4 ++ web/tests/conftest.py | 53 +++++++++++++-- web/tests/test_api.py | 109 +++++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 30 deletions(-) create mode 100644 web/b3desk/endpoints/api.py create mode 100644 web/tests/test_api.py diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index 91088da0..e4101b13 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -171,11 +171,13 @@ def setup_endpoints(app): import b3desk.endpoints.public import b3desk.endpoints.join import b3desk.endpoints.meetings + import b3desk.endpoints.api import b3desk.endpoints.meeting_files app.register_blueprint(b3desk.endpoints.public.bp) app.register_blueprint(b3desk.endpoints.join.bp) app.register_blueprint(b3desk.endpoints.meetings.bp) + app.register_blueprint(b3desk.endpoints.api.bp) app.register_blueprint(b3desk.endpoints.meeting_files.bp) diff --git a/web/b3desk/endpoints/api.py b/web/b3desk/endpoints/api.py new file mode 100644 index 00000000..e62cd1f2 --- /dev/null +++ b/web/b3desk/endpoints/api.py @@ -0,0 +1,28 @@ +from b3desk.models.users import get_or_create_user +from flask import Blueprint +from flask import request + +from .. import auth + + +bp = Blueprint("api", __name__) + + +@bp.route("/api/meetings") +@auth.token_auth("default", scopes_required=["profile", "email"]) +def api_meetings(): + client = auth.clients["default"] + access_token = auth._parse_access_token(request) + userinfo = client.userinfo_request(access_token).to_dict() + user = get_or_create_user(userinfo) + + return { + "meetings": [ + { + "name": m.name, + "moderator_url": m.get_signin_url("moderator"), + "attendee_url": m.get_signin_url("attendee"), + } + for m in user.meetings + ] + } diff --git a/web/b3desk/endpoints/meetings.py b/web/b3desk/endpoints/meetings.py index 3b1ad229..93960685 100644 --- a/web/b3desk/endpoints/meetings.py +++ b/web/b3desk/endpoints/meetings.py @@ -16,7 +16,6 @@ from b3desk.models import db from b3desk.models.meetings import get_quick_meeting_from_user_and_random_string from b3desk.models.meetings import Meeting -from b3desk.models.users import get_or_create_user from b3desk.models.users import User from flask import abort from flask import Blueprint @@ -50,31 +49,6 @@ def meeting_mailto_params(meeting, role): ).replace("\n", "%0D%0A") -@bp.route("/api/meetings") -@auth.token_auth(provider_name="default") -def api_meetings(): - # TODO: probably unused - if not auth.current_token_identity: - return redirect(url_for("public.index")) - - info = { - "given_name": auth.current_token_identity["given_name"], - "family_name": auth.current_token_identity["family_name"], - "email": auth.current_token_identity["email"], - } - user = get_or_create_user(info) - return { - "meetings": [ - { - "name": m.name, - "moderator_url": m.get_signin_url("moderator"), - "attendee_url": m.get_signin_url("attendee"), - } - for m in user.meetings - ] - } - - @bp.route("/meeting/mail", methods=["POST"]) def quick_mail_meeting(): #### Almost the same as quick meeting but we do not redirect to join diff --git a/web/b3desk/session.py b/web/b3desk/session.py index da59b94b..9456faa0 100644 --- a/web/b3desk/session.py +++ b/web/b3desk/session.py @@ -2,6 +2,7 @@ from b3desk.models.users import get_or_create_user from flask import abort +from flask import current_app from flask import g from flask import session from flask_pyoidc.user_session import UserSession @@ -12,6 +13,9 @@ def get_current_user(): user_session = UserSession(session) info = user_session.userinfo g.user = get_or_create_user(info) + current_app.logger.debug( + f"User authenticated with token: {user_session.access_token}" + ) return g.user diff --git a/web/tests/conftest.py b/web/tests/conftest.py index aadc240c..07223833 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -1,3 +1,4 @@ +import datetime import threading import time import uuid @@ -17,6 +18,21 @@ from b3desk.models import db +@pytest.fixture +def iam_user(iam_server): + iam_user = iam_server.models.User( + id="user_id", + emails=["alice@domain.tld"], + given_name="Alice", + user_name="Alice_user_name", + family_name="Cooper", + ) + iam_user.save() + + yield iam_user + iam_user.delete() + + @pytest.fixture def iam_client(iam_server): iam_client = iam_server.models.Client( @@ -36,6 +52,28 @@ def iam_client(iam_server): iam_client.delete() +@pytest.fixture +def iam_token(iam_server, iam_client, iam_user): + iam_token = iam_server.models.Token( + access_token="access_token_example", + audience=iam_client, + client=iam_client, + id="token_id", + issue_date=datetime.datetime.now(tz=datetime.UTC), + lifetime=36000, + refresh_token="refresh_token_example", + revokation_date=None, + scope=["openid", "profile", "email"], + subject=iam_user, + token_id="token_id", + type="access_token", + ) + iam_token.save() + + yield iam_token + iam_token.delete() + + @pytest.fixture def configuration(tmp_path, iam_server, iam_client, smtpd): smtpd.config.use_starttls = True @@ -115,19 +153,23 @@ def meeting(client_app, user): @pytest.fixture -def user(client_app): +def user(client_app, iam_user): from b3desk.models.users import User - user = User(email="alice@domain.tld", given_name="Alice", family_name="Cooper") + user = User( + email=iam_user.emails[0], + given_name=iam_user.given_name, + family_name=iam_user.family_name, + ) user.save() yield user @pytest.fixture -def authenticated_user(client_app, user): +def authenticated_user(client_app, user, iam_token, iam_server, iam_user): with client_app.session_transaction() as session: - session["access_token"] = "" + session["access_token"] = iam_token.access_token session["access_token_expires_at"] = "" session["current_provider"] = "default" session["id_token"] = "" @@ -142,6 +184,9 @@ def authenticated_user(client_app, user): } session["refresh_token"] = "" + iam_server.login(iam_user) + iam_server.consent(iam_user) + yield user diff --git a/web/tests/test_api.py b/web/tests/test_api.py new file mode 100644 index 00000000..aef6c6d2 --- /dev/null +++ b/web/tests/test_api.py @@ -0,0 +1,109 @@ +import datetime + + +def test_api_meetings_nominal(client_app, user, meeting, iam_token): + res = client_app.get( + "/api/meetings", headers={"Authorization": f"Bearer {iam_token.access_token}"} + ) + assert res.json["meetings"] + assert res.json["meetings"][0]["name"] == "meeting" + assert ( + f"/meeting/signin/{meeting.id}/creator/{user.id}/hash/" + in res.json["meetings"][0]["moderator_url"] + ) + assert ( + f"/meeting/signin/{meeting.id}/creator/{user.id}/hash/" + in res.json["meetings"][0]["attendee_url"] + ) + + +def test_api_meetings_no_token(client_app): + client_app.get("/api/meetings", status=401) + + +def test_api_meetings_invalid_token(client_app): + client_app.get( + "/api/meetings", headers={"Authorization": "Bearer invalid-token"}, status=403 + ) + + +def test_api_meetings_token_expired(client_app, iam_server, iam_client, iam_user, user): + iam_token = iam_server.models.Token( + access_token="access_token_example", + audience=iam_client, + client=iam_client, + id="token_id", + issue_date=datetime.datetime(2000, 1, 1, tzinfo=datetime.UTC), + lifetime=36000, + refresh_token="refresh_token_example", + revokation_date=None, + scope=["openid", "profile", "email"], + subject=iam_user, + token_id="token_id", + type="access_token", + ) + iam_token.save() + + client_app.get( + "/api/meetings", + headers={"Authorization": f"Bearer {iam_token.access_token}"}, + status=403, + ) + + iam_token.delete() + + +def test_api_meetings_client_id_missing_in_token_audience( + client_app, iam_server, iam_client, iam_user, user +): + iam_token = iam_server.models.Token( + access_token="access_token_example", + audience="some-other-audience", + client=iam_client, + id="token_id", + issue_date=datetime.datetime.now(tz=datetime.UTC), + lifetime=36000, + refresh_token="refresh_token_example", + revokation_date=None, + scope=["openid", "profile", "email"], + subject=iam_user, + token_id="token_id", + type="access_token", + ) + iam_token.save() + + client_app.get( + "/api/meetings", + headers={"Authorization": f"Bearer {iam_token.access_token}"}, + status=403, + ) + + iam_token.delete() + + +def test_api_meetings_missing_scope_in_token( + client_app, iam_server, iam_client, iam_user, user +): + iam_token = iam_server.models.Token( + access_token="access_token_example", + audience=iam_client, + client=iam_client, + id="token_id", + issue_date=datetime.datetime.now(tz=datetime.UTC), + lifetime=36000, + refresh_token="refresh_token_example", + revokation_date=None, + scope=["openid"], + subject=iam_user, + token_id="token_id", + type="access_token", + ) + iam_token.save() + + client_app.get( + "/api/meetings", + headers={"Authorization": f"Bearer {iam_token.access_token}"}, + status=403, + ) + + iam_token.delete() From 95acd9944e59f06181972089a75a4f89e39efcfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 12 Mar 2024 10:13:56 +0100 Subject: [PATCH 169/213] documentation: API documentation --- documentation/_static/keycloak-audience.png | Bin 0 -> 41776 bytes documentation/developers/api.md | 63 ++++++++++++++++++++ documentation/developers/index.rst | 1 + web/tests/conftest.py | 2 +- web/tests/test_api.py | 6 +- 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 documentation/_static/keycloak-audience.png create mode 100644 documentation/developers/api.md diff --git a/documentation/_static/keycloak-audience.png b/documentation/_static/keycloak-audience.png new file mode 100644 index 0000000000000000000000000000000000000000..cc53fb39a10bdc20826b521f2fa5db4662b2c9ff GIT binary patch literal 41776 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
      wl3>5Gq?(KIQTWP0<1g?>6Z!Aw@z;3bw2V z&-yfOXrLWb!->bAF4{qF0Ai?duJ}Hh8X+71od&=Hl4Ak9w5IJ2?=k0`;EKtvi`DP^ zE&OAp@{>|b>G$o2X1V@lUuxoP*2>Y%Zhw0z93JJ45^r+f7xC8(a#pxADSf?2o+U0- zSK4L7B32y6uyX0+>Dntf$og6q+53N`0y*PyJmCz~SSD^opA34Sb7QWReU3R+g{>JF zncqA+bloRP+wu#oiI@>q3;tf4@2btQ^th~dx)Jal=TqMjiYx;IZ@M|gqD5#e2C6=o29| zBEGBwb7Kxwaw{XHDQO;2;AhTKZZ{438zEQfC-ej3l ztBO${rhHgilW|h~P;A=A(xqw$nf&%f#GVB>>E+P6j@YEg1UohX=-SWN-Q9h_>xkw8 zpk=OM9vJxOWW{hbl7;9bdlw^s{nsnWJY91y9_y9jT9#f;;?XayYkf2A{^+XJ4DX!T zCvN+FAwtYjl^YZSwU)6bf4~d8{W%~lO(y$6uF}pS8{Rud-0D+EE?UIip5mPA#81NC zQej&sJ_ZKKa zVSpygBQNB~BPdx{o1c-q#Nk z1G4Qx^;{q-nK4ePrY{uxIh#H{zr(K8uTtKG<(1_N0l>`Lf}`1%9CS5xzUk!ikB3xL z*bp?V%+oA#HlX8o*L8*GMt#j?cwD#U1V;-iwd(Q{CjRK?rrtg&X(AzzN;Gt(!}W#( zeu}&{I&fMM58j8=U#BoIsE@x&lOKizXgF}3lxHZcw2~4{ z;$6>#u`;t=Kx9SL&bX)Ha%J_rQ>8z|fy|=pGBu&f;{m5He2^QI8W-`;N!^e#$us2q zF}fIgrjUE!=|Q1t)@|l74B#_rs^p5zN<7Yi3Y%}3v7w#^O#O88@U^7H& zcD&4M+}e!kTLkOR2`6oTNKUHT#I{iU+vusV^fhEPvQsqv&RB5Cyd2)+)a9tyDwyRE z*!w2Fc-c;Al&eyGE4!tq1O!F$G(CJ~{a;|;3@)=HAe@!#pWYi72fLdv@i58 zsA47GYK`S1PQvX#rqBFaP8A!&qK{VaB7MRaZ{Ef@>HFvgF;76nKjJ>9JeO9{SVfdBBK?FM=x9D$9~RPMoBNtcr?pOnhjo2thk zzLcmg(m$C(7`BQ+j zilE#a6z36&teoV*%PQCz)ai+vV&9a>D#VXdx3l754;ShSY;jUvsq z_Cz}TiqRbufRJN z6vO_8KbwvZ$t;Lxl>1bWP{E=XgU8bT9L^xdkk`3{lZkuk@&`?M_GtJ50xG#xA6-}p z?1>%!{O~Bd|M4BQiQE3wO=(RY-s#0H+~y4j&t}>5_L?2^siTb1-f@-lUCM~hdw=ztl~l_?F>MD^J=6fjHv;$*^?+H5`VHEwuEE~65K=pw10+}KcK zJxKF6mHVx_73vf`=f~@PU!9vu!4D_UC$nK5dQexh{_s+S;A%fCKs8PKo z6jpmCrJsyvk74`-S_Xrnb+u5C-v5PvtB9&S68^DZ^4#BFKOlU6-saT08ET!RlxEpa zMuU25Uv)`KQvK^x7oGW1?ALtY!~6}Wl8KOn9AB&XH#76M?jk#h2?0G6242|k220g3 z9@EVh6L7vkC3@lMSCkc8ne-?KpN~Sn^8MdYa@*4VZ4n6~nD*}bi{>0)XKL$E$FB)c zZTX#B)QJ^0_%V-0(04yZyPSpbBw1)bt039PGn;Xf1AufWA|Z#(7w9p#mS(0<{@;}l zUr8S?bOF4p$rD-H+=$MpM7IX$H?a+1J@q5YOUt-(ovfP=7<&UR;p@p$dDzIS53lAA z=BwxgEl3a8PQ)XyA^x+(+IiY-m`D0#O$3&e#yv%3qFe`q_vF-??p!=I=xgz!pr9_k zo6P>|IN#sAcAK}TjO?ZEeD&f)OeOm$G}6sy%Tw|nFN1dcGgnXCHq0e#n|O zb;^l%XjJ0c3s`u4Tc6;9xl=+7kAo9K->X7k$!P8D@xEpFK?6U>$hm9nRNAd7&KL@Q zRK_V>C`QP5oUdX!Jhu{@p3+-%l=w5~tz&PYnwg&-$Xxk{fDA(u{iP`afok^fMat=Y z@ofI;G+$w2dwN+Q!XBpUQ!{*?hJ!4xSpeEe!SUbelJ z78ca$zeSpH23Q2^3fJ-&J_zA+RXcD{^$E`p{EOsFrI3Ua+v>r1sFycs$`qpc>ahnt((K5-y5yjq>N=jKvh2}E?9OSzug3lbxLd@m6lGeKhT zsYSc1S{DoNxZI~aM5D8Lpq#RVq#4tC-Y3M*8=8b|M((|kh^s5Z$jaj*slPV_Oozn# zyDM&^F(#)o-m8^l;6?Je`?=k42MC+c+4X&Xu)$tbJc7<@F6WUtCrfIWIfs9dEECCQ z@};zaXwv>(G?a+q$=>0i%ZwC$Hk181a&Vm+j25lbpU%>y=-fMW6ciMbB=nHuG|{uj z;8Mm1>(|=U!vmNN!+9e#7nc{I+puQD9vy80`rH}n^^Py3ZPXIDzXdws%a zfj7+#F?A;Vus$K@&a2mWdt=ndnUj-Xj(zXvY?HU16M$Po8Wi<;fIm(K=d`FDy8?YGR)MYjJShO;=JMY;&%fG~?PI$|chh2w6VxekzZ(4EIY^g7X(gbh zloxa3rsaT7cYwAYvUR?`Rmb2-;<9_^{xV47na%Yfz&=ZV(a6315ZS-)lm1KfZ->6F4guiflX%Bt4yISG( zqVH%H9<_jeX>-2V4Lpnn!FY8%i&-`w+ZaAo7UaD5Ady*GyN8d|#P2LF^4eYt%-HSO z!KcMfV>!afAi@|LnkU@Y!vkDw?2Jnc+7LZk_hho$d^o_~^oK}FPL+6YrI(~qN%G#r zknD%BsRb$|D~pJTjKK#OsBmL^lL_a^Y*XW?0_}U~?rrL6q9uy1=%>Coz{9+PVd`GL^7)4Xwm@@__rO7v` zb_VMD>!P4U>rtYImNl(fK0Q5E(w2+?uCK0oP95hDlN|aO!??>rLczoB__xh+y?kCuD4HqQelGEi@$Ca-&^x2RhxZ0L(0rH z$Utzx4$U6Pd&4Cu%V46eIJQ99*O{Xb7jN0K5TpCN_@_y6odkWL*%A3%nab!=c+tri zwV)YZTn%lB;2L;Vb{fWVzEw#sjWC71%*!;tSRlSOQWu}0buGR~H@PSW{GR3s65Dq$m|$yQF*L?|3dhah*2BoGxHMV- zyT3~|U#2VKgi{l$ouzSI%8!=$piO7PA%FzI*Tof0voapPLdtShX;b(DtrSl;zWxrD zb^hK=u0Dv+vxIW;ZazObEWI`?57)>7Uw^r)fD+d9GMQBRHPS*%o~DKP3OY4 zJf(Q4k?e~iJWkOY&>{MhTZgiqS><{>(b>$`*U^`LbDWNkGpW_vO29STqg>qOC}uBC zU}eyypxmGPA1#3I>xco9rnd8RW6|%MVVn9A*tgjp8cP}2l9AT$%7{?sv;4Sk@m+s% z4eaes{Npidrx(ETng2J+$xk9%} zA56zW*vvrsL6sd>>k`{^9P)8%8;a%fw^wy(PUOzT5Ei!Qpk6EAHcC0sT;%<4QtYd4 zrxP0jo(+1%idEU7-8}gQV)&z}5NiP~p_IM}Ckgz0kkH|a{@h8i2|L0#(QJ-&&f}_E z0+W><@o%h%y==Td=0dOc^Q&%n!njQfP1;P$h)NN9$hMY7uGa@*<7Wh16y9i-N{@a) zK?6lx;Oxmzx?oBC1ACMYEr=wp@R@wYoWwx`~;KgA^9*>~k? z<5cinQk!*)14n92q*|GSGV^&yX5z)6y+NB*PpA< z>`aRk(CMltp*@Et@kOZow%B4uL;-r4bOPOHkSd`bCs^RQ3Z#MC?QSnyj87Vn;ZWu( zQK?_dTRwPftaYPkj>H}gZ*!+9?xL@Mu1z}ucx{a_JqLw{?+5TOWdD*bL(zor`{b&# zA#ME=Jv#e^(=M?_+l6Y?sx@-h)iLjTH^GXB%}0}~{n(@OXB|!-A?W-lpYB}W&_FNB zE`G=WqjxCL(IOvRBP7?)1F#ImJ)%NJ`YNgaW}17X3^H z>Q+JsSj#b(7iRa!mQC#xWx0>GS9Mrhnr(46Wbv`aV;hr+6a3U^iXR}S7F(j)-0X_e zd5z@N<&vVPNV z@TNhji^=*ZB7qz0kL9}Zap}UrC^A2HdkIiPM6uBg4CKwzp}x*NHQZOui2k(4{ylcN zJvuf7a5%7gVLmy&v=TY{5wfZ=SOUKlA=fQTt@*C!gDetbS?+x7#q~Ask(IbH{3?%# z?x|)s7MubVV~OL=0pFjP8XJWaPQ@CdetKB6ZfHjD9+SL$0syq7P>uHxQa03N&-qr0 z%$oMciQfIYda$(`DB^?@vVj@A>Yyl?#?~ZQhVy!Ng?v@OD?Rlzy28#K^ffmqER2NK zhlx|lQbxW{kKp6SPeR|gRmgWK_ICH8oYHhv4NeMMm!V-OMpVF%7XmXIJ4qjZ#5+n^ z$V|<*;Zhkmsyp?eWvcDeW-X!E1?N66aMSFM4dtp|0wN;|XPo8cGB;RO8SGly5$~n$ zZkSFyacpNN;?oc%0 zZmBm1EVV>-7eAeRZnViQZerEHse166%)|2KA!pLt6>r^2mC3YA!yI9?`5HS{Yqk&eQq@=!7`23Sf?-a<*@#sprE1?3(WP;wHBKA1Q@-8)nEe@d(D%oN zjg1@;c92u95`5{)L8g0pn$V@wH0YvWE5!X!Q0koUCAbj{W zyIVKNVpHLUtn0!`$o}zGgyWW{dbHqE6_bup7u3m2dZPzP+&#~kk<_apdMPMHg~)vj z2}<~o??{snf9_-8FS?zVJz)QG`7I0>LMb=^ltSPXM~qAaSvhXY+v%%f*=;;bn2&i- zIU;s{1Cgq>i#U=mJKvKugE#L`a@v@UyX1RzUF|{RAke)GjI=#Zn}ctER)w^>F9hsg z9=gyqT)T$hFz|N9I5sfeoHBjkWlkPR)Y@zVIz^ubCW62YMQ`h@VkEgt0S(v?=Af*R{;=WMJ z`)P8^cPjpHi*fP+$xXp|6;3)B`a16el)ex^^-ku$%t!E0-N;ixb{2ekwrb3L;dt7M zR>tHU@5m@(DX+jUJqq;Mw!nHDGb3ecg25u&{TioZa`xBO$dM=13z#phCT3&j*upiP zEZah;HAdrx15v~d{AW*~tEPjT&?EPi=1RlS6i6Npa1#%<;9>tm$o0WwE}LVNd*)6A zm)v6=%m}&cz`|Rvu-hK)g7h+AH-@p z6STFp^{+rU7yzRzPoEe5e@i3&oi13d^VeIhhM1(>@WroRTwap!@wLh9?^$yOMMRLZ zv)7LS-o^^=4gbGI;R7`^lw+qagn;;wL=qAbo$KozV*pxOT77W*>aT(A2Sa@|7U@{F z#j}SDv2dvdJOKd#+Tr3Frst<8>lVB{U`SBV428dLG&g)Y_=;@}e^6p(Zw72Nm!iJi zaDi0lp_II-AW-{(ZFkCdYAQAi1d_UgwAz` z2iW=Q)4(>7D&TwzJpgPvBAZbBgd)f~}%aL7B8~uM%10w&u%78?)cU4OH;*R`F~ilSN=NBMic&)mX(CNjnxRS)q=P`HFQD|^ zix8xU(tBv3HwjHTgdz|?q$Gr1Gl%#4p84itW^U%0xtMcvF4lg|*~wXZ@Adnyb@p*f zm+-sZG}O;!ph@vy(Cp9LAdN&=lx0Fcw$N_sJ`!1=1q*AjEHqy*SZ zBiFy2T3j(zjg-)r>8y;tAUT!>?>W*TR7zhiLUlAjx8{>JE7M?w{{@5VV5e{61vVc0 z-m2QVM@q$nMjJ0b-_->@vm5AM8=3*_kC5cka*1+WK0P(U(|vaosh$#X>(C@;V^BSh zi=R7sbH~T-9kLA(S)Pr^e6MPxkQUHRXDmrQjL;N*SX{YrVgb`;4YWS~X#6a&HJFxAO`% zu&bL#gnflkhjw2dAGrH);mH^+gu5=Kf*06kCb?( zhDK_vu1y^q3L2#j2{G-Rc-m3q1@u`x&W^I(R5VlOl!%Nt0kL>{lCIrb{8LI*>jb;v za3^2@;UI+^G)>b9XFYTM&_yQc%391bn(!7&@0U`-4r*9Qs?Vs`d+%8op z*=B5UUk$-c3fH*W6V-$xpHZ0|F=9__=Mw$%3Vn|60(x=Q5{2tnc^8H0pHBZPutQ4; z?0}8^t^SUUR;&0r;l`G6-vUB<;?ztig6vlDrN`>d1E>WzwVbfEnXG9)g`>Hu^6HGS z!IPhi@!MKuu8{0k0g!`>!E$=ydNEfo!9h)=1P6%9eyOZ`@7v2O{o?duiDx$1;Tihu zLYXE?Dp(Dx{=&S8EPQPoszDoC-Lz^ z?D99ao|q=N|3ukdf7kxa#w35A4Uf)hje4tsys_i`mO#Io+^R!ex>@vq@Dl7zr{m|i zT-l2sAmZSWc@der#r!}eD@BqCs_j%`7ODef zzUREFlv@|E3ZeAlWR|Mx*G}6b#ePkFNW;v(LO;gYSQIclsJ~gIX2;rIKg10|hQ1&I zl+*mc(_qE_lBgCW;&Pt!{TS#XlB(=O{^)+d;7mnDL&~>dnLaXp9eo8<8afjpMkk)n zq|ApnN5{x2NW0q}te6ph?7HLqnxigibqw}vyYvFOn!-9r`Hu*T2O1-BRB^ z7kDTY-Lk^FEw8VOjF5xnd)a|XQf+xIY(1dG%*`X=)xlQz_RaEPF2`Bkd!C3Go`r@C zp9Gs-^v8g`(L%rU52_IVS2X1^9)x(Th;Qj5ih|6M(d*X4p`${+kkDnw?pv?_9@h9- z{sn8MAaY-($DGrZu&oH}`{b|H?~&Feh%?@j{$YwBCdr}<^2l@e`q}~3>r?_HD3&XUN6OHLRT~$LZ`#sBr{fFUiG&MQi0E@T#u?31J zLQfPQ3Yz6HQYjzG+L&t)&uXGr9=#Njs^^W5E_18Lc6_Dr#j}njP9|3UQD69s2*lNy zQ@-^+V)t5BjIS&!z13p@6>dBn-CUf);y0ocJUY6oV?U$o z^{i)7=olD1S$oK2aPn@xBTzxznUOZYF)yWVyQc&c*?dj+v zbI61|M*12YkE%bgmI`ZlC$he!{nlgIqbefehc-SPokUn~M{UAlF4|Rx{eH^%_E?gm z(a$HB>5OG&Z7gUytsd@u0<(ZN#$I$~;^lEV22*+pJ`Jmta^>Kyt+v}Bz+^r6! zB8E-Uz^4pqeX{@joicFsKL3)u97MZk_=ajQ8c|%XzLl?OWSTK>8q;-Pae^9*bsOGU zPm|s?S(AueS&L5{U5>WDcK$ymK~*^DmwNf8Q^D|E3=W+}q~w8!@WIc)9LW0DXAgU9 zydJ&3)a!HD7sxsax8_4c3L?+-)IpgZ-3ykt=fE0 zK98>V4Ub@KGi7Df%3o1^+3K4tX)>5QE)XSwz@VM--mtymI?U-zwqU4P{_yp}>amW# z=Guyj9D^(rw8MwNqUFX!6;Wbla}Rbf+mw2j@uBywySMs)^vk^C^tp%cA9jmsWEk|Q zkHOY=;CmuLZ>W4$O*bUJ(K(eYX=m17x1<%ee5Jx@y~dr;vhYba?$b>MlX!P#D%H%8 z${ZY~Sov|C6b~#k5eKbb3w)-v5UuZ8D^&1lPTGdVGaENf6LS@BlXQ-@Ho6wgoovlF zh7ZbP?u)nH@FlqEG@G#}+cP4F9jFiL4 z0F`T|Yw~a#)jL@V5!Ku~@&pe8ZKS=h?2ew%`X$m_T7`)o+p|Bvbf&jlB^RH~1GM#j z!<+x$OG`sNKkX$?#t@x@MVk)a_j#w4;s4;j%7T7pLpe){{5Gh)L5(0;r^vl6!2XrJ z#2HwG4+blS#sdtMwR8@iY~g>YCM6|}??uH7_x6Sc>6e$@>O!Un<<-;x|BWtBGXk-J zf;;K5>xs|-z@PuTayr4vFzu_Fn%YgoDdjiJ2J;lId(k4pT9;Q?NT?tHe|aERDpLt0 zw=~AOmYjL1BEy~`UIE-%DOmi+I)nS#4)LXhniX#uu))s$iq>kS?n|kwD+6*s-D{w1 z@ixe!ao&-cLUqD-n7atbYCTi@a%D0UA{JqCUp;uH+w?V&aM*ofk>pB&$Omk%5T6Lz z=G)Y}b(by#OK9{19=;a(yR(38s!e>?i6D=^70?x<@hugeN|&q#;UP)Ekdx4%LZc zFXMs(h9?K?vU_!_DJy^801>45`nk#EZ!M2Lw))I8AGYp&W2}4lEvAJz+t4>m`BQBs zW|zNvCo6owM7QHa64Y^VlbRNxV;n9JKxN%?&l(|{s@p`7u!+lqPA$#(BcBW%h|V0$ zyaK@mQq&3SL>@vZl}(~IJi1Hg;ztOa1S33z->#AD8Dyn|(vExtOM?4K<$`z@4&*R{ z=NY<9kyyLYaB9iLzOcIMt8O*@F#Tj8S1En>TcLsda81cQNAB-Fx1?zpI_@HZH@1BC z{mXJY?NO=S#|=&OmI|yxZcMw*Oe8;Kantk+ZNElv7YHU2v9{um4f3f^EN_gRlDw=s z(HB0!n5=n+V4oXNQ9lN^>LTx6(;S6=bseGBTqNs!f6j&?z4Zm!bi*;=*d@EEiDa#4 z#&`dfmhuED*K*~~%M8=bF1}&0GRcy2w3{9pVxY_f#UE%-|6=Ohrj+aRn5l`ZvJ3xJ2kk39!`4&%ksuzKMg_ft(84I4|t$2fKt zy0q6Eeyhhji?WscFxTSpE4K9+C4$v=sfwE2C|*?>LwaNoGAYM-w# zQ_|1Vn2qS(E_3xl+sJK#l~p3+i+cZkXM5jW-%NNTU} zSZ*zhg!PhRpqY!|ZkzKj3@_(&XNTY!o-GvC`Y?&^#S4|&cpC`Kd2?9o+Id?K0-9v5 z^Tv!CC>B?JLZy&wnkWZyerZxF`~z7yPRhkO?lSJhB@In#Ji>L0Q;NaQlk>-|s9)bW zKF7zZUIlEx42=>G!2DrZGoN&r877Mk_LA>UeXVZ=Xr*hDy zT4>E=BV&XE$=`2V{^co(pVf&#KI#*6caf5W$*x&fA#i*`i6u{q_2{X-H}u<4xtIt% zohvqIkO&3)1xf#zlkvP~ijd2!9aP;rMuQ(6PIm{e`h}za=t5Td?5I~!{bxZ4!gFj5 zMST2r4^H|{6e5UV*Q3sIS`ki7|8mzpB+xVG?t7mJ%ZXROv~2^_u7qt*%r%-ho5R;Y zGmn!ZA;co=G6^V%%#Q(H>++4Z-n$xY@@=8VT1(`VQet|B*1~us&iXa{Nyp(j_5-+H zC=$KhutiF~Hu;XNLynx_ytzT7|88eV=7$KmToSS7(oD2r`QF+TQf;9kFj<`r+iKxPoi!5)Zmuf+J+v^>Hsky!gT?s){BMY!<4n&fjIKhemSNaKZiDe$K>dzn zMQhH-(GHDKiEZv-!zL)8JwCbt%;noG<$j!)is0k2XvVw0+W6%L8nezCptj%>$6!XJ z+r}82&otJx(jR%t4RPPhV`cbXh!1m@n*{YiRI%IhxJQVXPnb%!^uFg;6HhMv| zKVfcXM(qVIw!V;wBgkVf<%0TIHzK1pO;6K~CN`nv!PJ(HTcFT(_(3~P8n($+AA|Qj z7IFfe@o}*RK6)?t5q0`HDtr=<8sf!6%{QXxE2)cfyW6)tR$Lpf0oeONvil_%TNZuC z{#!Vc7UE8k3c*NP0lkqt)!5T)y{xO(p8Zbu+dDf63&+Acm)b;Xa;kuJn^G4j-mru3 zr(e}6Mbqf9lm6g6A;z-jmmVt70~Opiq;(KT6NW(*BC2lEN{`JY}YER zw!6L9e~>)y=oy^h(7fra$pF(8uuiJax> z^5B=pCAU|dmVS|v;MFtr%jFn?OBAu8DtmdaZUy|xlp>M=3;5$36NGQA*ufr#bs>)y zXQd9OW9GSm7HmL^$)S(20S9ef+yih4nh@oGbl~rZ&F%bB7>E#nK_>Zk(G2xJi)a4- coe_A#hVOr!2e^Iby9OL;%34ZgPtAh<4H~>2*8l(j literal 0 HcmV?d00001 diff --git a/documentation/developers/api.md b/documentation/developers/api.md new file mode 100644 index 00000000..94b03654 --- /dev/null +++ b/documentation/developers/api.md @@ -0,0 +1,63 @@ +# API publique + +## Points d'accès + +Une API publique est disponible sur `/api/meetings`. Cette API est utilisée par les greffons Thunderbird et Outlook. + +Les réponses sont au format json sous la forme suivante : + +```json +{ + "meetings": [ + { + "attendee_url": "https://example.tld/meeting/signin/1234/creator/5678/hash/27955e8c3a16ecadbb3cf61df7806a04ce6fd18c", + "moderator_url": "https://example.tld/meeting/signin/1234/creator/5678/hash/3e73643801d5013d389f8fc610e258aba38e597d", + "name": "Mon Séminaire" + } + ] +} +``` + +## Authentification + +L'authentification à l'API se fait en passant un jeton OIDC émis par le serveur d'identifié configuré dans `OIDC_ISSUER`. + +### Tests + +On peut tester le bon fonctionnement de l'API comme ceci (*en renseignant au préalable la variable `$TOKEN`*). + +```bash +curl -s -H "Authorization:Bearer $TOKEN" https://example.tld/api/meetings +``` + +### Problèmes de connexion + +#### Jeton manquant + +Lorsque le jeton d'identification n'a pas été fourni dans la requête, l'API retourne des codes d'erreur HTTP 401. + +#### Jeton expiré + +Lorsque le jeton est expiré, l'API retourne des codes d'erreur HTTP 403. + +#### Mauvaise audience du jeton + +Lorsque l'audience du jeton est incorrecte, l'API retourne des codes d'erreur 403. +Dans les faits il faut s'assurer que le paramètre `aud` du jeton contient bien l'ID client OIDC de l'application, définie dans le paramètre `OIDC_CLIENT_ID` de l'application, et prenant par défaut la valeur `bbb-vision`. +On peut vérifier l'audience d'un token avec des outils tels que [jwt.io](https://jwt.io). + +Par défaut, keycloak ne remplit pas l'audience du jeton avec l'ID client. +Il est nécessaire d'effectuer une configuration dans la console d'aministration de keycloak comme celle-ci (c'est un exemple, d'autres sont probablement possibles) : + +1. Se rendre sur la console d'administration de keycloak +2. Se rendre dans le menu « Clients » +3. Sélectionner le client « bbb-visio » +4. Se rendre dans l'onglet « Mappers » +5. Cliquer sur le bouton « Create » +6. Remplir (par exemple) le formulaire comme ceci: + - Name: bbb-visio-audience + - Mapper Type: audience + - Included Custom Audience: bbb-visio + + ![keycloak](../_static/keycloak-audience.png) +7. Générer un nouveau token et vérifier qu'il contient bien la valeur `bbb-vision` dans le paramètre `aud`. diff --git a/documentation/developers/index.rst b/documentation/developers/index.rst index cf86420d..2b091a14 100644 --- a/documentation/developers/index.rst +++ b/documentation/developers/index.rst @@ -6,6 +6,7 @@ Cette documentation est à destination des développeurs. .. toctree:: :maxdepth: 2 + api contributing ci dockerPersistence diff --git a/web/tests/conftest.py b/web/tests/conftest.py index 07223833..5ca31a5f 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -59,7 +59,7 @@ def iam_token(iam_server, iam_client, iam_user): audience=iam_client, client=iam_client, id="token_id", - issue_date=datetime.datetime.now(tz=datetime.UTC), + issue_date=datetime.datetime.now(tz=datetime.timezone.utc), lifetime=36000, refresh_token="refresh_token_example", revokation_date=None, diff --git a/web/tests/test_api.py b/web/tests/test_api.py index aef6c6d2..18896e0d 100644 --- a/web/tests/test_api.py +++ b/web/tests/test_api.py @@ -33,7 +33,7 @@ def test_api_meetings_token_expired(client_app, iam_server, iam_client, iam_user audience=iam_client, client=iam_client, id="token_id", - issue_date=datetime.datetime(2000, 1, 1, tzinfo=datetime.UTC), + issue_date=datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), lifetime=36000, refresh_token="refresh_token_example", revokation_date=None, @@ -61,7 +61,7 @@ def test_api_meetings_client_id_missing_in_token_audience( audience="some-other-audience", client=iam_client, id="token_id", - issue_date=datetime.datetime.now(tz=datetime.UTC), + issue_date=datetime.datetime.now(tz=datetime.timezone.utc), lifetime=36000, refresh_token="refresh_token_example", revokation_date=None, @@ -89,7 +89,7 @@ def test_api_meetings_missing_scope_in_token( audience=iam_client, client=iam_client, id="token_id", - issue_date=datetime.datetime.now(tz=datetime.UTC), + issue_date=datetime.datetime.now(tz=datetime.timezone.utc), lifetime=36000, refresh_token="refresh_token_example", revokation_date=None, From 4aa69c7ef95c0826a37a5e0269f289fc46b06c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 12 Mar 2024 10:47:41 +0100 Subject: [PATCH 170/213] tests: use random_token utility method from pytest-iam --- web/tests/conftest.py | 15 +-------------- web/tests/test_api.py | 38 ++++---------------------------------- 2 files changed, 5 insertions(+), 48 deletions(-) diff --git a/web/tests/conftest.py b/web/tests/conftest.py index 5ca31a5f..5753c6ba 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -1,4 +1,3 @@ -import datetime import threading import time import uuid @@ -54,22 +53,10 @@ def iam_client(iam_server): @pytest.fixture def iam_token(iam_server, iam_client, iam_user): - iam_token = iam_server.models.Token( - access_token="access_token_example", - audience=iam_client, + iam_token = iam_server.random_token( client=iam_client, - id="token_id", - issue_date=datetime.datetime.now(tz=datetime.timezone.utc), - lifetime=36000, - refresh_token="refresh_token_example", - revokation_date=None, - scope=["openid", "profile", "email"], subject=iam_user, - token_id="token_id", - type="access_token", ) - iam_token.save() - yield iam_token iam_token.delete() diff --git a/web/tests/test_api.py b/web/tests/test_api.py index 18896e0d..9b9cc177 100644 --- a/web/tests/test_api.py +++ b/web/tests/test_api.py @@ -28,21 +28,11 @@ def test_api_meetings_invalid_token(client_app): def test_api_meetings_token_expired(client_app, iam_server, iam_client, iam_user, user): - iam_token = iam_server.models.Token( - access_token="access_token_example", - audience=iam_client, + iam_token = iam_server.random_token( client=iam_client, - id="token_id", - issue_date=datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), - lifetime=36000, - refresh_token="refresh_token_example", - revokation_date=None, - scope=["openid", "profile", "email"], subject=iam_user, - token_id="token_id", - type="access_token", + issue_date=datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc), ) - iam_token.save() client_app.get( "/api/meetings", @@ -57,20 +47,10 @@ def test_api_meetings_client_id_missing_in_token_audience( client_app, iam_server, iam_client, iam_user, user ): iam_token = iam_server.models.Token( - access_token="access_token_example", - audience="some-other-audience", client=iam_client, - id="token_id", - issue_date=datetime.datetime.now(tz=datetime.timezone.utc), - lifetime=36000, - refresh_token="refresh_token_example", - revokation_date=None, - scope=["openid", "profile", "email"], subject=iam_user, - token_id="token_id", - type="access_token", + audience="some-other-audience", ) - iam_token.save() client_app.get( "/api/meetings", @@ -85,20 +65,10 @@ def test_api_meetings_missing_scope_in_token( client_app, iam_server, iam_client, iam_user, user ): iam_token = iam_server.models.Token( - access_token="access_token_example", - audience=iam_client, client=iam_client, - id="token_id", - issue_date=datetime.datetime.now(tz=datetime.timezone.utc), - lifetime=36000, - refresh_token="refresh_token_example", - revokation_date=None, - scope=["openid"], subject=iam_user, - token_id="token_id", - type="access_token", + scope=["openid"], ) - iam_token.save() client_app.get( "/api/meetings", From 8b6a473bd3cfa963642836e472b92370d82bfd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 12 Mar 2024 11:15:09 +0100 Subject: [PATCH 171/213] chore: bump to pytest-iam 0.0.7 --- poetry.lock | 11 ++++++----- pyproject.toml | 2 +- web/requirements.dev.txt | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index c3b4eba2..ac77bf6f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiosmtpd" @@ -2123,13 +2123,13 @@ python-dotenv = ">=0.9.1" [[package]] name = "pytest-iam" -version = "0.0.6" +version = "0.0.7" description = "A fully functional OAUTH2 / OpenID Connect (OIDC) server to be used in your testsuite" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "pytest_iam-0.0.6-py3-none-any.whl", hash = "sha256:7a42d3491f555b305553db47ecdda994c0f4ad14c645fa67df47bb4e2c1f9ba7"}, - {file = "pytest_iam-0.0.6.tar.gz", hash = "sha256:560f0dab98a629cc41be552e653fad1137d30df95aee6beed45cb68e4ff3efab"}, + {file = "pytest_iam-0.0.7-py3-none-any.whl", hash = "sha256:88e2eabfea2a0be100d9034006ba89229f40bd9c9fdd02a06d26d24e54d3f25f"}, + {file = "pytest_iam-0.0.7.tar.gz", hash = "sha256:4fed93ad7196794305f2d295ddc03f32650ad270683f49292ba183823ba7fab2"}, ] [package.dependencies] @@ -2237,6 +2237,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2908,4 +2909,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0" -content-hash = "734f9c51927f3647e221caaea8d9490c75fa0195320b8cb673b091638b0cff69" +content-hash = "bcd25ed05168e4695c2b6a05ec7dd5a8587935fe959d126c85e8f1e9a1654988" diff --git a/pyproject.toml b/pyproject.toml index e44f0860..0264f7e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ pre-commit = "^3.1.1" pytest = "^7.4.0" pytest-cov = "^4.1.0" pytest-dotenv = "^0.5.2" -pytest-iam = "^0.0.6" +pytest-iam = "^0.0.7" pytest-mock = "^3.11.1" wsgidav = "^4.3.0" pytest-smtpd = "^0.1.0" diff --git a/web/requirements.dev.txt b/web/requirements.dev.txt index b7261e41..6941f24b 100644 --- a/web/requirements.dev.txt +++ b/web/requirements.dev.txt @@ -48,7 +48,7 @@ pyflakes==3.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pyquery==2.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-cov==4.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-dotenv==0.5.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -pytest-iam==0.0.6 ; python_full_version >= "3.8.1" and python_version < "4.0" +pytest-iam==0.0.7 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-mock==3.12.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-smtpd==0.1.0 ; python_full_version >= "3.8.1" and python_version < "4" pytest==7.4.4 ; python_full_version >= "3.8.1" and python_version < "4.0" From 94f1caffe095ff67398154e2013491bd50b6acda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 12 Mar 2024 15:56:20 +0100 Subject: [PATCH 172/213] chore: version number fix --- pyproject.toml | 2 +- web/b3desk/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0264f7e0..196366a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "b3desk" -version = "1.1.2" +version = "1.1.3" description = "Outil de visioconférence pour les agents de l'Education Nationale et de l'Etat en général." authors = ["Your Name "] readme = "README.md" diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index e4101b13..76cb05a5 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -27,7 +27,7 @@ from .utils import model_converter -__version__ = "1.1.2" +__version__ = "1.1.3" LANGUAGES = ["en", "fr"] From 4fabb980d3dcd424bae10b2968f080802cfd24da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 12 Mar 2024 16:23:30 +0100 Subject: [PATCH 173/213] feat: use OIDC_INTROSPECTION_AUTH_METHOD to configure flask-oidc introspection endpoint authentication method --- web/b3desk/__init__.py | 6 ++++++ web/b3desk/settings.py | 33 +++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index 76cb05a5..61ac6812 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -195,6 +195,9 @@ def setup_oidc(app): client_id=app.config["OIDC_CLIENT_ID"], client_secret=app.config["OIDC_CLIENT_SECRET"], token_endpoint_auth_method=app.config["OIDC_CLIENT_AUTH_METHOD"], + introspection_endpoint_auth_method=app.config[ + "OIDC_INTROSPECTION_AUTH_METHOD" + ], post_logout_redirect_uris=[logout_url], ), auth_request_params={"scope": app.config["OIDC_SCOPES"]}, @@ -208,6 +211,9 @@ def setup_oidc(app): token_endpoint_auth_method=app.config.get( "OIDC_ATTENDEE_CLIENT_AUTH_METHOD" ), + introspection_endpoint_auth_method=app.config.get( + "OIDC_ATTENDEE_INTROSPECTION_AUTH_METHOD" + ), post_logout_redirect_uris=[logout_url], ), auth_request_params={"scope": app.config["OIDC_ATTENDEE_SCOPES"]}, diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index 8774ec29..cdce9053 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -299,9 +299,6 @@ def get_allowed_mime_types_server_side( def get_oidc_scopes(cls, oidc_scopes: List[str], info: ValidationInfo) -> str: return oidc_scopes.split(",") if isinstance(oidc_scopes, str) else oidc_scopes - OIDC_INTROSPECTION_AUTH_METHOD: str = "client_secret_post" - """Probablement un relicat de flask-oidc, semble inutilisé.""" - OIDC_USERINFO_HTTP_METHOD: str = "POST" """Méthode ``GET`` ou ``POST`` à utiliser pour les requêtes sur le point d’entrée *UserInfo* du serveur d’identité. @@ -336,11 +333,12 @@ def get_oidc_scopes(cls, oidc_scopes: List[str], info: ValidationInfo) -> str: des organisateurs.""" OIDC_CLIENT_AUTH_METHOD: Optional[str] = "client_secret_post" - """Méthode de communication avec le point d’entrée - ``token_introspection_uri`` du serveur d’identité des organisateurs. + """Méthode de communication avec le point d’entrée ``token_endpoint`` du + serveur d’identité des organisateurs.""" - Surcharge probablement ``OIDC_INTROSPECTION_AUTH_METHOD``. - """ + OIDC_INTROSPECTION_AUTH_METHOD: str = "client_secret_basic" + """Méthode de communication avec le point d’entrée d’introspection + ``token_introspection`` du serveur d’identité des organisateurs.""" # TODO: replace by OIDCAuthentication.redirect_uri_config OIDC_REDIRECT_URI: Optional[str] = None @@ -385,13 +383,20 @@ def get_oidc_scopes(cls, oidc_scopes: List[str], info: ValidationInfo) -> str: """ OIDC_ATTENDEE_CLIENT_AUTH_METHOD: Optional[str] = None - """Méthode de communication avec le point d’entrée - ``token_introspection_uri`` du serveur d’identité des participants - authentifiés. Surcharge probablement ``OIDC_INTROSPECTION_AUTH_METHOD``. + """Méthode de communication avec le point d’entrée ``token_endpoint`` du + serveur d’identité des participants authentifiés. Si non renseigné, prend la valeur de ``OIDC_CLIENT_AUTH_METHOD``. """ + OIDC_ATTENDEE_INTROSPECTION_AUTH_METHOD: str = "client_secret_basic" + """Méthode de communication avec le point d’entrée d’introspection + ``token_introspection`` du serveur d’identité des participants + authentifiés. + + Si non renseigné, prend la valeur de ``OIDC_INTROSPECTION_AUTH_METHOD``. + """ + OIDC_ATTENDEE_USERINFO_HTTP_METHOD: Optional[str] = None """Méthode ``GET`` ou ``POST`` à utiliser pour les requêtes sur le point d’entrée *UserInfo* du serveur d’identité. @@ -441,6 +446,14 @@ def get_attendee_client_auth_method( ) -> str: return attendee_client_auth_method or info.data.get("OIDC_CLIENT_AUTH_METHOD") + @field_validator("OIDC_ATTENDEE_INTROSPECTION_AUTH_METHOD") + def get_attendee_introspection_endpoint_auth_method( + cls, attendee_introspection_endpoint_auth_method: str, info: ValidationInfo + ) -> str: + return attendee_introspection_endpoint_auth_method or info.data.get( + "OIDC_INTROSPECTION_AUTH_METHOD" + ) + @field_validator("OIDC_ATTENDEE_USERINFO_HTTP_METHOD") def get_attendee_userinfo_http_method( cls, attendee_userinfo_http_method: str, info: ValidationInfo From 097cdbabe67b15eeb6d9bc850a286a735fa8d337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 13 Mar 2024 08:56:47 +0100 Subject: [PATCH 174/213] chore: update dependencies --- poetry.lock | 763 +++++++++++++++++++-------------------- web/requirements.app.txt | 44 +-- web/requirements.dev.txt | 40 +- web/requirements.doc.txt | 44 +-- 4 files changed, 435 insertions(+), 456 deletions(-) diff --git a/poetry.lock b/poetry.lock index ac77bf6f..96917b39 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "aiosmtpd" -version = "1.4.4.post2" +version = "1.4.5" description = "aiosmtpd - asyncio based SMTP server" optional = false -python-versions = "~=3.7" +python-versions = ">=3.8" files = [ - {file = "aiosmtpd-1.4.4.post2-py3-none-any.whl", hash = "sha256:f821fe424b703b2ea391dc2df11d89d2afd728af27393e13cf1a3530f19fdc5e"}, - {file = "aiosmtpd-1.4.4.post2.tar.gz", hash = "sha256:f9243b7dfe00aaf567da8728d891752426b51392174a34d2cf5c18053b63dcbc"}, + {file = "aiosmtpd-1.4.5-py3-none-any.whl", hash = "sha256:a196922f1903e54c4d37c53415b7613056d39e2b1e8249f324b9ee7a439be0f1"}, + {file = "aiosmtpd-1.4.5.tar.gz", hash = "sha256:78d7b14f859ad0e6de252b47f9cf1ca6f1c82a8b0f10a9e39bec7e915a6aa5fe"}, ] [package.dependencies] @@ -384,13 +384,13 @@ zstd = ["zstandard (==0.22.0)"] [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -643,63 +643,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.dependencies] @@ -710,43 +710,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.0" +version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434"}, - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc"}, - {file = "cryptography-42.0.0-cp37-abi3-win32.whl", hash = "sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4"}, - {file = "cryptography-42.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0"}, - {file = "cryptography-42.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221"}, - {file = "cryptography-42.0.0-cp39-abi3-win32.whl", hash = "sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b"}, - {file = "cryptography-42.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce"}, - {file = "cryptography-42.0.0.tar.gz", hash = "sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, ] [package.dependencies] @@ -880,13 +880,13 @@ pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "flask" -version = "3.0.1" +version = "3.0.2" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.8" files = [ - {file = "flask-3.0.1-py3-none-any.whl", hash = "sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13"}, - {file = "flask-3.0.1.tar.gz", hash = "sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403"}, + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, ] [package.dependencies] @@ -935,13 +935,13 @@ Flask = "*" [[package]] name = "flask-migrate" -version = "4.0.5" +version = "4.0.7" description = "SQLAlchemy database migrations for Flask applications using Alembic." optional = false python-versions = ">=3.6" files = [ - {file = "Flask-Migrate-4.0.5.tar.gz", hash = "sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1"}, - {file = "Flask_Migrate-4.0.5-py3-none-any.whl", hash = "sha256:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef"}, + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, ] [package.dependencies] @@ -1048,12 +1048,13 @@ python-dateutil = ">=2.7" [[package]] name = "future" -version = "0.18.3" +version = "1.0.0" description = "Clean single-source support for Python 3 and 2" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ - {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, + {file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"}, + {file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"}, ] [[package]] @@ -1149,13 +1150,13 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "identify" -version = "2.5.33" +version = "2.5.35" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, - {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] [package.extras] @@ -1185,32 +1186,32 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.1" +version = "7.0.2" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, + {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, + {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" -version = "6.1.1" +version = "6.3.0" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, + {file = "importlib_resources-6.3.0-py3-none-any.whl", hash = "sha256:783407aa1cd05550e3aa123e8f7cfaebee35ffa9cb0242919e2d1e4172222705"}, + {file = "importlib_resources-6.3.0.tar.gz", hash = "sha256:166072a97e86917a9025876f34286f549b9caf1d10b35a1b372bffa1600c6569"}, ] [package.dependencies] @@ -1218,7 +1219,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] +testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -1261,13 +1262,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.14" +version = "0.9.22" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "json5-0.9.14-py2.py3-none-any.whl", hash = "sha256:740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f"}, - {file = "json5-0.9.14.tar.gz", hash = "sha256:9ed66c3a6ca3510a976a9ef9b8c0787de24802724ab1860bc0153c7fdd589b02"}, + {file = "json5-0.9.22-py3-none-any.whl", hash = "sha256:6621007c70897652f8b5d03885f732771c48d1925591ad989aa80c7e0e5ad32f"}, + {file = "json5-0.9.22.tar.gz", hash = "sha256:b729bde7650b2196a35903a597d2b704b8fdf8648bfb67368cfb79f1174a17bd"}, ] [package.extras] @@ -1402,13 +1403,13 @@ source = ["Cython (>=3.0.7)"] [[package]] name = "mako" -version = "1.3.1" +version = "1.3.2" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.1-py3-none-any.whl", hash = "sha256:463f03e04559689adaee25e0967778d6ad41285ed607dc1e7df0dd4e4df81f9e"}, - {file = "Mako-1.3.1.tar.gz", hash = "sha256:baee30b9c61718e093130298e678abed0dbfa1b411fcc4c1ab4df87cd631a0f2"}, + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, ] [package.dependencies] @@ -1445,71 +1446,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.4" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, - {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, - {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, - {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, - {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, - {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, - {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, - {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -1646,13 +1647,13 @@ types = ["types-requests"] [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -1668,28 +1669,28 @@ files = [ [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1857,18 +1858,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.3" typing-extensions = ">=4.6.1" [package.extras] @@ -1876,116 +1877,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.3" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, ] [package.dependencies] @@ -1993,19 +1968,23 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.1.0" +version = "2.2.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, - {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, ] [package.dependencies] pydantic = ">=2.3.0" python-dotenv = ">=0.21.0" +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "pyflakes" version = "3.2.0" @@ -2175,13 +2154,13 @@ dev = ["flake8", "isort", "mypy", "tox"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -2203,13 +2182,13 @@ cli = ["click (>=5.0)"] [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] @@ -2237,7 +2216,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2274,17 +2252,17 @@ files = [ [[package]] name = "redis" -version = "5.0.1" +version = "5.0.3" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, - {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, + {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"}, + {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"}, ] [package.dependencies] -async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} [package.extras] hiredis = ["hiredis (>=1.0.0)"] @@ -2313,19 +2291,19 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "setuptools" -version = "69.0.3" +version = "69.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2559,57 +2537,57 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "1.4.51" +version = "1.4.52" description = "Database Abstraction Library" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "SQLAlchemy-1.4.51-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:1a09d5bd1a40d76ad90e5570530e082ddc000e1d92de495746f6257dc08f166b"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2be4e6294c53f2ec8ea36486b56390e3bcaa052bf3a9a47005687ccf376745d1"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca484ca11c65e05639ffe80f20d45e6be81fbec7683d6c9a15cd421e6e8b340"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0535d5b57d014d06ceeaeffd816bb3a6e2dddeb670222570b8c4953e2d2ea678"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af55cc207865d641a57f7044e98b08b09220da3d1b13a46f26487cc2f898a072"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-win32.whl", hash = "sha256:7af40425ac535cbda129d9915edcaa002afe35d84609fd3b9d6a8c46732e02ee"}, - {file = "SQLAlchemy-1.4.51-cp310-cp310-win_amd64.whl", hash = "sha256:8d1d7d63e5d2f4e92a39ae1e897a5d551720179bb8d1254883e7113d3826d43c"}, - {file = "SQLAlchemy-1.4.51-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eaeeb2464019765bc4340214fca1143081d49972864773f3f1e95dba5c7edc7d"}, - {file = "SQLAlchemy-1.4.51-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7deeae5071930abb3669b5185abb6c33ddfd2398f87660fafdb9e6a5fb0f3f2f"}, - {file = "SQLAlchemy-1.4.51-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0892e7ac8bc76da499ad3ee8de8da4d7905a3110b952e2a35a940dab1ffa550e"}, - {file = "SQLAlchemy-1.4.51-cp311-cp311-win32.whl", hash = "sha256:50e074aea505f4427151c286955ea025f51752fa42f9939749336672e0674c81"}, - {file = "SQLAlchemy-1.4.51-cp311-cp311-win_amd64.whl", hash = "sha256:3b0cd89a7bd03f57ae58263d0f828a072d1b440c8c2949f38f3b446148321171"}, - {file = "SQLAlchemy-1.4.51-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a33cb3f095e7d776ec76e79d92d83117438b6153510770fcd57b9c96f9ef623d"}, - {file = "SQLAlchemy-1.4.51-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cacc0b2dd7d22a918a9642fc89840a5d3cee18a0e1fe41080b1141b23b10916"}, - {file = "SQLAlchemy-1.4.51-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245c67c88e63f1523e9216cad6ba3107dea2d3ee19adc359597a628afcabfbcb"}, - {file = "SQLAlchemy-1.4.51-cp312-cp312-win32.whl", hash = "sha256:8e702e7489f39375601c7ea5a0bef207256828a2bc5986c65cb15cd0cf097a87"}, - {file = "SQLAlchemy-1.4.51-cp312-cp312-win_amd64.whl", hash = "sha256:0525c4905b4b52d8ccc3c203c9d7ab2a80329ffa077d4bacf31aefda7604dc65"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:1980e6eb6c9be49ea8f89889989127daafc43f0b1b6843d71efab1514973cca0"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec7a0ed9b32afdf337172678a4a0e6419775ba4e649b66f49415615fa47efbd"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352df882088a55293f621328ec33b6ffca936ad7f23013b22520542e1ab6ad1b"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:86a22143a4001f53bf58027b044da1fb10d67b62a785fc1390b5c7f089d9838c"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c37bc677690fd33932182b85d37433845de612962ed080c3e4d92f758d1bd894"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-win32.whl", hash = "sha256:d0a83afab5e062abffcdcbcc74f9d3ba37b2385294dd0927ad65fc6ebe04e054"}, - {file = "SQLAlchemy-1.4.51-cp36-cp36m-win_amd64.whl", hash = "sha256:a61184c7289146c8cff06b6b41807c6994c6d437278e72cf00ff7fe1c7a263d1"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:3f0ef620ecbab46e81035cf3dedfb412a7da35340500ba470f9ce43a1e6c423b"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c55040d8ea65414de7c47f1a23823cd9f3fad0dc93e6b6b728fee81230f817b"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ef80328e3fee2be0a1abe3fe9445d3a2e52a1282ba342d0dab6edf1fef4707"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f8cafa6f885a0ff5e39efa9325195217bb47d5929ab0051636610d24aef45ade"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f2df79a46e130235bc5e1bbef4de0583fb19d481eaa0bffa76e8347ea45ec6"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-win32.whl", hash = "sha256:f2e5b6f5cf7c18df66d082604a1d9c7a2d18f7d1dbe9514a2afaccbb51cc4fc3"}, - {file = "SQLAlchemy-1.4.51-cp37-cp37m-win_amd64.whl", hash = "sha256:5e180fff133d21a800c4f050733d59340f40d42364fcb9d14f6a67764bdc48d2"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7d8139ca0b9f93890ab899da678816518af74312bb8cd71fb721436a93a93298"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb18549b770351b54e1ab5da37d22bc530b8bfe2ee31e22b9ebe650640d2ef12"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e699466106d09f028ab78d3c2e1f621b5ef2c8694598242259e4515715da7c"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2ad16880ccd971ac8e570550fbdef1385e094b022d6fc85ef3ce7df400dddad3"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b97fd5bb6b7c1a64b7ac0632f7ce389b8ab362e7bd5f60654c2a418496be5d7f"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-win32.whl", hash = "sha256:cecb66492440ae8592797dd705a0cbaa6abe0555f4fa6c5f40b078bd2740fc6b"}, - {file = "SQLAlchemy-1.4.51-cp38-cp38-win_amd64.whl", hash = "sha256:39b02b645632c5fe46b8dd30755682f629ffbb62ff317ecc14c998c21b2896ff"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:b03850c290c765b87102959ea53299dc9addf76ca08a06ea98383348ae205c99"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e646b19f47d655261b22df9976e572f588185279970efba3d45c377127d35349"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3cf56cc36d42908495760b223ca9c2c0f9f0002b4eddc994b24db5fcb86a9e4"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0d661cff58c91726c601cc0ee626bf167b20cc4d7941c93c5f3ac28dc34ddbea"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3823dda635988e6744d4417e13f2e2b5fe76c4bf29dd67e95f98717e1b094cad"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-win32.whl", hash = "sha256:b00cf0471888823b7a9f722c6c41eb6985cf34f077edcf62695ac4bed6ec01ee"}, - {file = "SQLAlchemy-1.4.51-cp39-cp39-win_amd64.whl", hash = "sha256:a055ba17f4675aadcda3005df2e28a86feb731fdcc865e1f6b4f209ed1225cba"}, - {file = "SQLAlchemy-1.4.51.tar.gz", hash = "sha256:e7908c2025eb18394e32d65dd02d2e37e17d733cdbe7d78231c2b6d7eb20cdb9"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-win32.whl", hash = "sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94"}, + {file = "SQLAlchemy-1.4.52-cp310-cp310-win_amd64.whl", hash = "sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef"}, + {file = "SQLAlchemy-1.4.52-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d"}, + {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0"}, + {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb"}, + {file = "SQLAlchemy-1.4.52-cp311-cp311-win32.whl", hash = "sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e"}, + {file = "SQLAlchemy-1.4.52-cp311-cp311-win_amd64.whl", hash = "sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12"}, + {file = "SQLAlchemy-1.4.52-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c"}, + {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554"}, + {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b"}, + {file = "SQLAlchemy-1.4.52-cp312-cp312-win32.whl", hash = "sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126"}, + {file = "SQLAlchemy-1.4.52-cp312-cp312-win_amd64.whl", hash = "sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-win32.whl", hash = "sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd"}, + {file = "SQLAlchemy-1.4.52-cp36-cp36m-win_amd64.whl", hash = "sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-win32.whl", hash = "sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782"}, + {file = "SQLAlchemy-1.4.52-cp37-cp37m-win_amd64.whl", hash = "sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-win32.whl", hash = "sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e"}, + {file = "SQLAlchemy-1.4.52-cp38-cp38-win_amd64.whl", hash = "sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-win32.whl", hash = "sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821"}, + {file = "SQLAlchemy-1.4.52-cp39-cp39-win_amd64.whl", hash = "sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680"}, + {file = "SQLAlchemy-1.4.52.tar.gz", hash = "sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296"}, ] [package.dependencies] @@ -2694,39 +2672,40 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] name = "tzdata" -version = "2023.4" +version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, - {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2743,13 +2722,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] @@ -2763,18 +2742,18 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "waitress" -version = "2.1.2" +version = "3.0.0" description = "Waitress WSGI server" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "waitress-2.1.2-py3-none-any.whl", hash = "sha256:7500c9625927c8ec60f54377d590f67b30c8e70ef4b8894214ac6e4cad233d2a"}, - {file = "waitress-2.1.2.tar.gz", hash = "sha256:780a4082c5fbc0fde6a2fcfe5e26e6efc1e8f425730863c04085769781f51eba"}, + {file = "waitress-3.0.0-py3-none-any.whl", hash = "sha256:2a06f242f4ba0cc563444ca3d1998959447477363a2d7e9b8b4d75d35cfd1669"}, + {file = "waitress-3.0.0.tar.gz", hash = "sha256:005da479b04134cdd9dd602d1ee7c49d79de0537610d653674cc6cbde222b8a1"}, ] [package.extras] docs = ["Sphinx (>=1.8.1)", "docutils", "pylons-sphinx-themes (>=1.0.9)"] -testing = ["coverage (>=5.0)", "pytest", "pytest-cover"] +testing = ["coverage (>=5.0)", "pytest", "pytest-cov"] [[package]] name = "wcwidth" @@ -2893,18 +2872,18 @@ email = ["email-validator"] [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.0-py3-none-any.whl", hash = "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79"}, + {file = "zipp-3.18.0.tar.gz", hash = "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" diff --git a/web/requirements.app.txt b/web/requirements.app.txt index fe2cf2f5..55097f06 100644 --- a/web/requirements.app.txt +++ b/web/requirements.app.txt @@ -1,7 +1,7 @@ alembic==1.13.1 ; python_full_version >= "3.8.1" and python_version < "4.0" amqp==5.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" annotated-types==0.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -async-timeout==4.0.3 ; python_full_version >= "3.8.1" and python_full_version <= "3.11.2" +async-timeout==4.0.3 ; python_full_version >= "3.8.1" and python_full_version < "3.11.3" babel==2.14.0 ; python_full_version >= "3.8.1" and python_version < "4.0" backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" backports-zoneinfo[tzdata]==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" @@ -9,7 +9,7 @@ billiard==4.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" blinker==1.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" cachelib==0.9.0 ; python_full_version >= "3.8.1" and python_version < "4.0" celery==5.3.6 ; python_full_version >= "3.8.1" and python_version < "4.0" -certifi==2023.11.17 ; python_full_version >= "3.8.1" and python_version < "4.0" +certifi==2024.2.2 ; python_full_version >= "3.8.1" and python_version < "4.0" cffi==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" and platform_python_implementation != "PyPy" charset-normalizer==3.3.2 ; python_full_version >= "3.8.1" and python_version < "4.0" click-didyoumean==0.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -17,55 +17,55 @@ click-plugins==1.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" click-repl==0.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" click==8.1.7 ; python_full_version >= "3.8.1" and python_version < "4.0" colorama==0.4.6 ; python_full_version >= "3.8.1" and python_version < "4.0" and platform_system == "Windows" -cryptography==42.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +cryptography==42.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_version < "4.0" filetype==1.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-babel==4.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-caching==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -flask-migrate==4.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" +flask-migrate==4.0.7 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-pyoidc==3.14.3 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-sqlalchemy==3.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-uploads==0.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-wtf==1.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -flask==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -future==0.18.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +flask==3.0.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +future==1.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" greenlet==3.0.3 ; python_full_version >= "3.8.1" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version < "4.0" gunicorn==21.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" idna==3.6 ; python_full_version >= "3.8.1" and python_version < "4.0" -importlib-metadata==7.0.1 ; python_full_version >= "3.8.1" and python_version < "3.10" -importlib-resources==6.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +importlib-metadata==7.0.2 ; python_full_version >= "3.8.1" and python_version < "3.10" +importlib-resources==6.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" itsdangerous==2.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" jinja2==3.1.3 ; python_full_version >= "3.8.1" and python_version < "4.0" kombu==5.3.5 ; python_full_version >= "3.8.1" and python_version < "4.0" lxml==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -mako==1.3.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -markupsafe==2.1.4 ; python_full_version >= "3.8.1" and python_version < "4.0" +mako==1.3.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" netaddr==0.10.1 ; python_full_version >= "3.8.1" and python_version < "4.0" oic==1.6.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -packaging==23.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" prompt-toolkit==3.0.43 ; python_full_version >= "3.8.1" and python_version < "4.0" psycopg2==2.9.9 ; python_full_version >= "3.8.1" and python_version < "4.0" pycparser==2.21 ; python_full_version >= "3.8.1" and python_version < "4.0" and platform_python_implementation != "PyPy" pycryptodomex==3.20.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic-core==2.14.6 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic-settings==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic==2.5.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic-core==2.16.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic-settings==2.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic==2.6.4 ; python_full_version >= "3.8.1" and python_version < "4.0" pyjwkest==1.4.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +python-dateutil==2.9.0.post0 ; python_full_version >= "3.8.1" and python_version < "4.0" python-dotenv==1.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -pytz==2023.3.post1 ; python_full_version >= "3.8.1" and python_version < "4.0" -redis==5.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +pytz==2024.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +redis==5.0.3 ; python_full_version >= "3.8.1" and python_version < "4.0" requests==2.31.0 ; python_full_version >= "3.8.1" and python_version < "4.0" six==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" sqlalchemy-json==0.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" sqlalchemy-utils==0.41.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -sqlalchemy==1.4.51 ; python_full_version >= "3.8.1" and python_version < "4.0" -typing-extensions==4.9.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -tzdata==2023.4 ; python_full_version >= "3.8.1" and python_version < "4.0" -urllib3==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +sqlalchemy==1.4.52 ; python_full_version >= "3.8.1" and python_version < "4.0" +typing-extensions==4.10.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +tzdata==2024.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +urllib3==2.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" vine==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" wcwidth==0.2.13 ; python_full_version >= "3.8.1" and python_version < "4.0" webdavclient3==3.14.6 ; python_full_version >= "3.8.1" and python_version < "4.0" werkzeug==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" wtforms==3.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -zipp==3.17.0 ; python_full_version >= "3.8.1" and python_version < "3.10" +zipp==3.18.0 ; python_full_version >= "3.8.1" and python_version < "3.10" diff --git a/web/requirements.dev.txt b/web/requirements.dev.txt index 6941f24b..30b6f713 100644 --- a/web/requirements.dev.txt +++ b/web/requirements.dev.txt @@ -1,6 +1,6 @@ -aiosmtpd==1.4.4.post2 ; python_full_version >= "3.8.1" and python_version < "4.0" -atpublic==4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -attrs==23.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +aiosmtpd==1.4.5 ; python_full_version >= "3.8.1" and python_version < "4" +atpublic==4.0 ; python_full_version >= "3.8.1" and python_version < "4" +attrs==23.2.0 ; python_full_version >= "3.8.1" and python_version < "4" authlib==1.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" beautifulsoup4==4.12.3 ; python_full_version >= "3.8.1" and python_version < "4" black==23.12.1 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -10,9 +10,9 @@ cffi==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4" and platf cfgv==3.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" click==8.1.7 ; python_full_version >= "3.8.1" and python_version < "4.0" colorama==0.4.6 ; python_full_version >= "3.8.1" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") -coverage==7.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -coverage[toml]==7.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -cryptography==42.0.0 ; python_full_version >= "3.8.1" and python_version < "4" +coverage==7.4.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +coverage[toml]==7.4.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +cryptography==42.0.5 ; python_full_version >= "3.8.1" and python_version < "4" cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_version < "4.0" distlib==0.3.8 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -22,23 +22,23 @@ filelock==3.13.1 ; python_full_version >= "3.8.1" and python_version < "4.0" flake8==7.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-webtest==0.1.4 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-wtf==1.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -flask==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +flask==3.0.2 ; python_full_version >= "3.8.1" and python_version < "4.0" freezegun==1.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -identify==2.5.33 ; python_full_version >= "3.8.1" and python_version < "4.0" -importlib-metadata==7.0.1 ; python_full_version >= "3.8.1" and python_version < "3.10" +identify==2.5.35 ; python_full_version >= "3.8.1" and python_version < "4.0" +importlib-metadata==7.0.2 ; python_full_version >= "3.8.1" and python_version < "3.10" iniconfig==2.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" itsdangerous==2.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" jinja2==3.1.3 ; python_full_version >= "3.8.1" and python_version < "4.0" -json5==0.9.14 ; python_full_version >= "3.8.1" and python_version < "4.0" +json5==0.9.22 ; python_full_version >= "3.8.1" and python_version < "4.0" lxml==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -markupsafe==2.1.4 ; python_full_version >= "3.8.1" and python_version < "4.0" +markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" nodeenv==1.8.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -packaging==23.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pathspec==0.12.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -platformdirs==4.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pluggy==1.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +platformdirs==4.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +pluggy==1.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" portpicker==1.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pre-commit==3.5.0 ; python_full_version >= "3.8.1" and python_version < "4.0" psutil==5.9.8 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -52,20 +52,20 @@ pytest-iam==0.0.7 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-mock==3.12.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-smtpd==0.1.0 ; python_full_version >= "3.8.1" and python_version < "4" pytest==7.4.4 ; python_full_version >= "3.8.1" and python_version < "4.0" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +python-dateutil==2.9.0.post0 ; python_full_version >= "3.8.1" and python_version < "4.0" python-dotenv==1.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -setuptools==69.0.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +setuptools==69.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" six==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" smtpdfix==0.5.1 ; python_full_version >= "3.8.1" and python_version < "4" soupsieve==2.5 ; python_full_version >= "3.8.1" and python_version < "4" tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version <= "3.11.0a6" -typing-extensions==4.9.0 ; python_full_version >= "3.8.1" and python_version < "3.11" -virtualenv==20.25.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -waitress==2.1.2 ; python_full_version >= "3.8.1" and python_version < "4" +typing-extensions==4.10.0 ; python_full_version >= "3.8.1" and python_version < "3.11" +virtualenv==20.25.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +waitress==3.0.0 ; python_full_version >= "3.8.1" and python_version < "4" webob==1.8.7 ; python_full_version >= "3.8.1" and python_version < "4" webtest==3.0.0 ; python_full_version >= "3.8.1" and python_version < "4" werkzeug==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" wsgidav==4.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" wtforms==3.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -zipp==3.17.0 ; python_full_version >= "3.8.1" and python_version < "3.10" +zipp==3.18.0 ; python_full_version >= "3.8.1" and python_version < "3.10" diff --git a/web/requirements.doc.txt b/web/requirements.doc.txt index f51d0b9d..c7706be6 100644 --- a/web/requirements.doc.txt +++ b/web/requirements.doc.txt @@ -2,7 +2,7 @@ alabaster==0.7.13 ; python_full_version >= "3.8.1" and python_version < "4.0" alembic==1.13.1 ; python_full_version >= "3.8.1" and python_version < "4.0" amqp==5.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" annotated-types==0.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -async-timeout==4.0.3 ; python_full_version >= "3.8.1" and python_full_version <= "3.11.2" +async-timeout==4.0.3 ; python_full_version >= "3.8.1" and python_full_version < "3.11.3" autodoc-pydantic==2.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" babel==2.14.0 ; python_full_version >= "3.8.1" and python_version < "4.0" backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" @@ -11,7 +11,7 @@ billiard==4.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" blinker==1.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" cachelib==0.9.0 ; python_full_version >= "3.8.1" and python_version < "4.0" celery==5.3.6 ; python_full_version >= "3.8.1" and python_version < "4.0" -certifi==2023.11.17 ; python_full_version >= "3.8.1" and python_version < "4.0" +certifi==2024.2.2 ; python_full_version >= "3.8.1" and python_version < "4.0" cffi==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" and platform_python_implementation != "PyPy" charset-normalizer==3.3.2 ; python_full_version >= "3.8.1" and python_version < "4.0" click-didyoumean==0.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -19,52 +19,52 @@ click-plugins==1.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" click-repl==0.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" click==8.1.7 ; python_full_version >= "3.8.1" and python_version < "4.0" colorama==0.4.6 ; python_full_version >= "3.8.1" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") -cryptography==42.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +cryptography==42.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_version < "4.0" docutils==0.20.1 ; python_full_version >= "3.8.1" and python_version < "4.0" filetype==1.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-babel==4.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-caching==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -flask-migrate==4.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" +flask-migrate==4.0.7 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-pyoidc==3.14.3 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-sqlalchemy==3.0.5 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-uploads==0.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-wtf==1.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -flask==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -future==0.18.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +flask==3.0.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +future==1.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" greenlet==3.0.3 ; python_full_version >= "3.8.1" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version < "4.0" gunicorn==21.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" idna==3.6 ; python_full_version >= "3.8.1" and python_version < "4.0" imagesize==1.4.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -importlib-metadata==7.0.1 ; python_full_version >= "3.8.1" and python_version < "3.10" -importlib-resources==6.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +importlib-metadata==7.0.2 ; python_full_version >= "3.8.1" and python_version < "3.10" +importlib-resources==6.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" itsdangerous==2.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" jinja2==3.1.3 ; python_full_version >= "3.8.1" and python_version < "4.0" kombu==5.3.5 ; python_full_version >= "3.8.1" and python_version < "4.0" lxml==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -mako==1.3.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +mako==1.3.2 ; python_full_version >= "3.8.1" and python_version < "4.0" markdown-it-py==3.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -markupsafe==2.1.4 ; python_full_version >= "3.8.1" and python_version < "4.0" +markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" mdit-py-plugins==0.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" mdurl==0.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" myst-parser==2.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" netaddr==0.10.1 ; python_full_version >= "3.8.1" and python_version < "4.0" oic==1.6.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -packaging==23.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" prompt-toolkit==3.0.43 ; python_full_version >= "3.8.1" and python_version < "4.0" psycopg2==2.9.9 ; python_full_version >= "3.8.1" and python_version < "4.0" pycparser==2.21 ; python_full_version >= "3.8.1" and python_version < "4.0" and platform_python_implementation != "PyPy" pycryptodomex==3.20.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic-core==2.14.6 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic-settings==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pydantic==2.5.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic-core==2.16.3 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic-settings==2.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +pydantic==2.6.4 ; python_full_version >= "3.8.1" and python_version < "4.0" pygments==2.17.2 ; python_full_version >= "3.8.1" and python_version < "4.0" pyjwkest==1.4.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_version < "4.0" +python-dateutil==2.9.0.post0 ; python_full_version >= "3.8.1" and python_version < "4.0" python-dotenv==1.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -pytz==2023.3.post1 ; python_full_version >= "3.8.1" and python_version < "4.0" +pytz==2024.1 ; python_full_version >= "3.8.1" and python_version < "4.0" pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -redis==5.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +redis==5.0.3 ; python_full_version >= "3.8.1" and python_version < "4.0" requests==2.31.0 ; python_full_version >= "3.8.1" and python_version < "4.0" six==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" snowballstemmer==2.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -80,13 +80,13 @@ sphinxcontrib-qthelp==1.0.3 ; python_full_version >= "3.8.1" and python_version sphinxcontrib-serializinghtml==1.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" sqlalchemy-json==0.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" sqlalchemy-utils==0.41.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -sqlalchemy==1.4.51 ; python_full_version >= "3.8.1" and python_version < "4.0" -typing-extensions==4.9.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -tzdata==2023.4 ; python_full_version >= "3.8.1" and python_version < "4.0" -urllib3==2.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +sqlalchemy==1.4.52 ; python_full_version >= "3.8.1" and python_version < "4.0" +typing-extensions==4.10.0 ; python_full_version >= "3.8.1" and python_version < "4.0" +tzdata==2024.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +urllib3==2.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" vine==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" wcwidth==0.2.13 ; python_full_version >= "3.8.1" and python_version < "4.0" webdavclient3==3.14.6 ; python_full_version >= "3.8.1" and python_version < "4.0" werkzeug==3.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" wtforms==3.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -zipp==3.17.0 ; python_full_version >= "3.8.1" and python_version < "3.10" +zipp==3.18.0 ; python_full_version >= "3.8.1" and python_version < "3.10" From 6d63c2791d6513deedc3063a40bce743d0d8464e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 13 Mar 2024 08:58:28 +0100 Subject: [PATCH 175/213] chore: pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- web/b3desk/settings.py | 6 +++--- web/tests/test_default.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ecc6692f..a4992f48 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.2.2' + rev: 'v0.3.2' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index cdce9053..7f3fd9b2 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -264,9 +264,9 @@ def get_allowed_mime_types_server_side( return allowed_mime_types_server_side - ACCEPTED_FILES_CLIENT_SIDE: Optional[ - str - ] = "image/*,.pdf,.doc,.docx,.htm,.html,.odp,.ods,.odt,.ppt,.pptx,.xls,.xlsx" + ACCEPTED_FILES_CLIENT_SIDE: Optional[str] = ( + "image/*,.pdf,.doc,.docx,.htm,.html,.odp,.ods,.odt,.ppt,.pptx,.xls,.xlsx" + ) """Liste de mime-types autorisés par le navigateur pour le téléversement des fichiers, séparés par des virgules. diff --git a/web/tests/test_default.py b/web/tests/test_default.py index b9754102..7c178881 100644 --- a/web/tests/test_default.py +++ b/web/tests/test_default.py @@ -117,9 +117,9 @@ def test_documentation__authenticated_user(client_app, authenticated_user): def test_documentation_with_redirection(client_app): - client_app.app.config["DOCUMENTATION_LINK"][ - "url" - ] = "https://documentation_redirect.ion" + client_app.app.config["DOCUMENTATION_LINK"]["url"] = ( + "https://documentation_redirect.ion" + ) client_app.app.config["DOCUMENTATION_LINK"]["is_external"] = True response = client_app.get("/documentation", status=302) From 43404ffab4f2f3d68f88b354eaf20753d37af7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 13 Mar 2024 09:54:00 +0100 Subject: [PATCH 176/213] chore: run pre-commit in GHA --- .github/workflows/Check_lint.yml | 25 ++++++------------------- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/Check_lint.yml b/.github/workflows/Check_lint.yml index 8207030b..837a303e 100644 --- a/.github/workflows/Check_lint.yml +++ b/.github/workflows/Check_lint.yml @@ -1,4 +1,4 @@ -name: Check lint +name: Run linters on: pull_request: @@ -11,23 +11,10 @@ on: - main jobs: - black: - name: Check black has been run + linter: + name: run pre-commit runs-on: ubuntu-latest steps: - - name: Setup git - uses: actions/checkout@v3 - - - name: Setup python - uses: actions/setup-python@v4 - with: - python-version: 3.9 - - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - pip install -r web/requirements.app.txt - pip install -r web/requirements.dev.txt - - - name: Check black has nothing to do - run: black . --check + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - uses: pre-commit/action@v3.0.1 diff --git a/pyproject.toml b/pyproject.toml index 196366a4..cdf67060 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,7 @@ markers = [ "no_scheme: cloud auth responds without any scheme for url", ] -[tool.ruff] +[tool.ruff.lint] ignore = [ "E501", # line too long "E722", # bare expect From 31a70d40de76c19c25c79b7c108d75ddf54d53d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 13 Mar 2024 10:07:45 +0100 Subject: [PATCH 177/213] chore: use isort instead of reoder-python-imports --- .pre-commit-config.yaml | 9 ++++---- web/b3desk/__init__.py | 13 ++++++----- web/b3desk/endpoints/api.py | 4 ++-- web/b3desk/endpoints/join.py | 16 +++++++------- web/b3desk/endpoints/meeting_files.py | 14 ++++++------ web/b3desk/endpoints/meetings.py | 22 +++++++++---------- web/b3desk/endpoints/public.py | 1 - web/b3desk/models/bbb.py | 3 ++- web/b3desk/models/meetings.py | 5 +++-- web/b3desk/models/users.py | 3 ++- web/b3desk/session.py | 3 ++- web/b3desk/settings.py | 5 +++-- web/b3desk/utils.py | 3 ++- web/migrations/alembic_helpers.py | 2 +- ...1094e771bd3f_create_meeting_files_table.py | 2 +- .../versions/54f71a7705a8_initial_tables2.py | 1 + ...0646_add_user_nextcloud_connection_info.py | 2 +- .../7d80b9223a1e_guestpolicy_migration.py | 2 +- ...ecfb10_add_last_connection_utc_datetime.py | 2 +- .../9aac3b5e1582_welcome_message_unbound.py | 1 + ...710a_remove_obsolete_meeting_attributes.py | 2 +- web/misc/gunicorn.py | 2 +- web/tests/conftest.py | 5 +++-- web/tests/meeting/test_meeting.py | 3 ++- web/tests/test_authentication.py | 1 + web/tests/test_user.py | 5 +++-- 26 files changed, 72 insertions(+), 59 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4992f48..9c96a50f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,11 +15,12 @@ repos: - id: end-of-file-fixer exclude: "\\.svg$|\\.map$|\\.min\\.css$|\\.min\\.js$|\\.po$|\\.pot$" - id: check-toml - - repo: https://github.com/asottile/reorder_python_imports - rev: v3.12.0 + - repo: https://github.com/pycqa/isort + rev: "5.13.2" hooks: - - id: reorder-python-imports - args: ["--application-directories", "b3desk"] + - id: isort + name: isort (python) + args: ["--force-single-line-imports", "--line-length", "120"] - repo: https://github.com/PyCQA/docformatter rev: v1.7.5 hooks: diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index 61ac6812..13f95d99 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -11,8 +11,6 @@ import logging import os -from b3desk.settings import MainSettings -from b3desk.utils import is_rie from flask import Flask from flask import render_template from flask import request @@ -25,6 +23,9 @@ from flask_wtf.csrf import CSRFProtect from jinja2 import StrictUndefined +from b3desk.settings import MainSettings +from b3desk.utils import is_rie + from .utils import model_converter __version__ = "1.1.3" @@ -107,8 +108,8 @@ def setup_database(app): def setup_jinja(app): - from b3desk.session import has_user_session from b3desk.session import get_current_user + from b3desk.session import has_user_session if app.debug or app.testing: app.jinja_env.undefined = StrictUndefined @@ -168,11 +169,11 @@ def internal_error(error): def setup_endpoints(app): with app.app_context(): - import b3desk.endpoints.public - import b3desk.endpoints.join - import b3desk.endpoints.meetings import b3desk.endpoints.api + import b3desk.endpoints.join import b3desk.endpoints.meeting_files + import b3desk.endpoints.meetings + import b3desk.endpoints.public app.register_blueprint(b3desk.endpoints.public.bp) app.register_blueprint(b3desk.endpoints.join.bp) diff --git a/web/b3desk/endpoints/api.py b/web/b3desk/endpoints/api.py index e62cd1f2..2ae3fe35 100644 --- a/web/b3desk/endpoints/api.py +++ b/web/b3desk/endpoints/api.py @@ -1,9 +1,9 @@ -from b3desk.models.users import get_or_create_user from flask import Blueprint from flask import request -from .. import auth +from b3desk.models.users import get_or_create_user +from .. import auth bp = Blueprint("api", __name__) diff --git a/web/b3desk/endpoints/join.py b/web/b3desk/endpoints/join.py index 85bc6e76..d5bc8865 100644 --- a/web/b3desk/endpoints/join.py +++ b/web/b3desk/endpoints/join.py @@ -1,13 +1,7 @@ from datetime import datetime -from b3desk.forms import JoinMailMeetingForm -from b3desk.forms import JoinMeetingForm -from b3desk.models import db -from b3desk.models.meetings import get_mail_meeting -from b3desk.models.meetings import get_meeting_from_meeting_id_and_user_id -from b3desk.models.meetings import Meeting -from flask import abort from flask import Blueprint +from flask import abort from flask import current_app from flask import flash from flask import redirect @@ -16,13 +10,19 @@ from flask import url_for from flask_babel import lazy_gettext as _ +from b3desk.forms import JoinMailMeetingForm +from b3desk.forms import JoinMeetingForm +from b3desk.models import db +from b3desk.models.meetings import Meeting +from b3desk.models.meetings import get_mail_meeting +from b3desk.models.meetings import get_meeting_from_meeting_id_and_user_id + from .. import auth from ..session import get_authenticated_attendee_fullname from ..session import get_current_user from ..session import has_user_session from ..session import meeting_owner_needed - bp = Blueprint("join", __name__) diff --git a/web/b3desk/endpoints/meeting_files.py b/web/b3desk/endpoints/meeting_files.py index c67c47ea..7e3879f4 100644 --- a/web/b3desk/endpoints/meeting_files.py +++ b/web/b3desk/endpoints/meeting_files.py @@ -7,13 +7,8 @@ import filetype import requests -from b3desk.forms import MeetingFilesForm -from b3desk.models import db -from b3desk.models.meetings import Meeting -from b3desk.models.meetings import MeetingFiles -from b3desk.models.meetings import MeetingFilesExternal -from flask import abort from flask import Blueprint +from flask import abort from flask import current_app from flask import flash from flask import jsonify @@ -30,11 +25,16 @@ from webdav3.exceptions import WebDavException from werkzeug.utils import secure_filename +from b3desk.forms import MeetingFilesForm +from b3desk.models import db +from b3desk.models.meetings import Meeting +from b3desk.models.meetings import MeetingFiles +from b3desk.models.meetings import MeetingFilesExternal + from .. import auth from ..session import get_current_user from ..session import meeting_owner_needed - bp = Blueprint("meeting_files", __name__) diff --git a/web/b3desk/endpoints/meetings.py b/web/b3desk/endpoints/meetings.py index 93960685..01340b7d 100644 --- a/web/b3desk/endpoints/meetings.py +++ b/web/b3desk/endpoints/meetings.py @@ -8,17 +8,8 @@ # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. -from b3desk.forms import EndMeetingForm -from b3desk.forms import MeetingForm -from b3desk.forms import MeetingWithRecordForm -from b3desk.forms import RecordingForm -from b3desk.forms import ShowMeetingForm -from b3desk.models import db -from b3desk.models.meetings import get_quick_meeting_from_user_and_random_string -from b3desk.models.meetings import Meeting -from b3desk.models.users import User -from flask import abort from flask import Blueprint +from flask import abort from flask import current_app from flask import flash from flask import redirect @@ -27,6 +18,16 @@ from flask import url_for from flask_babel import lazy_gettext as _ +from b3desk.forms import EndMeetingForm +from b3desk.forms import MeetingForm +from b3desk.forms import MeetingWithRecordForm +from b3desk.forms import RecordingForm +from b3desk.forms import ShowMeetingForm +from b3desk.models import db +from b3desk.models.meetings import Meeting +from b3desk.models.meetings import get_quick_meeting_from_user_and_random_string +from b3desk.models.users import User + from .. import auth from ..session import get_current_user from ..session import meeting_owner_needed @@ -34,7 +35,6 @@ from ..utils import is_valid_email from ..utils import send_quick_meeting_mail - bp = Blueprint("meetings", __name__) diff --git a/web/b3desk/endpoints/public.py b/web/b3desk/endpoints/public.py index 5d4c9a71..7f82f4b1 100644 --- a/web/b3desk/endpoints/public.py +++ b/web/b3desk/endpoints/public.py @@ -12,7 +12,6 @@ from ..templates.content import FAQ_CONTENT from .meetings import meeting_mailto_params - bp = Blueprint("public", __name__) diff --git a/web/b3desk/models/bbb.py b/web/b3desk/models/bbb.py index 0efa6cd1..9db7fd4a 100644 --- a/web/b3desk/models/bbb.py +++ b/web/b3desk/models/bbb.py @@ -14,11 +14,12 @@ from xml.etree import ElementTree import requests -from b3desk.tasks import background_upload from flask import current_app from flask import render_template from flask import url_for +from b3desk.tasks import background_upload + class BBB: """Interface to BBB API.""" diff --git a/web/b3desk/models/meetings.py b/web/b3desk/models/meetings.py index 4457c632..7b531454 100644 --- a/web/b3desk/models/meetings.py +++ b/web/b3desk/models/meetings.py @@ -12,12 +12,13 @@ from datetime import datetime from datetime import timedelta -from b3desk.utils import get_random_alphanumeric_string -from b3desk.utils import secret_key from flask import current_app from flask import url_for from sqlalchemy_utils import StringEncryptedType +from b3desk.utils import get_random_alphanumeric_string +from b3desk.utils import secret_key + from . import db from .users import User diff --git a/web/b3desk/models/users.py b/web/b3desk/models/users.py index 968946f3..c40d470f 100644 --- a/web/b3desk/models/users.py +++ b/web/b3desk/models/users.py @@ -15,9 +15,10 @@ from urllib.parse import urlunparse import requests -from b3desk.utils import secret_key from flask import current_app +from b3desk.utils import secret_key + from . import db diff --git a/web/b3desk/session.py b/web/b3desk/session.py index 9456faa0..2394e9b7 100644 --- a/web/b3desk/session.py +++ b/web/b3desk/session.py @@ -1,12 +1,13 @@ from functools import wraps -from b3desk.models.users import get_or_create_user from flask import abort from flask import current_app from flask import g from flask import session from flask_pyoidc.user_session import UserSession +from b3desk.models.users import get_or_create_user + def get_current_user(): if "user" not in g: diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index 7f3fd9b2..e238284f 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -4,14 +4,15 @@ from typing import List from typing import Optional -from b3desk.constants import DEFAULT_EMAIL_WHITELIST from flask_babel import lazy_gettext as _ +from pydantic import ValidationInfo from pydantic import computed_field from pydantic import field_validator -from pydantic import ValidationInfo from pydantic_settings import BaseSettings from pydantic_settings import SettingsConfigDict +from b3desk.constants import DEFAULT_EMAIL_WHITELIST + AVAILABLE_WORDINGS = { "MEETING": {"cours": "cours", "reunion": "réunion", "seminaire": "séminaire"}, "MEETINGS": {"cours": "cours", "reunion": "réunions", "seminaire": "séminaires"}, diff --git a/web/b3desk/utils.py b/web/b3desk/utils.py index 13790723..bbb01faa 100644 --- a/web/b3desk/utils.py +++ b/web/b3desk/utils.py @@ -5,7 +5,6 @@ from email.message import EmailMessage from email.mime.text import MIMEText -from b3desk.models import db from flask import abort from flask import current_app from flask import render_template @@ -15,6 +14,8 @@ from netaddr import IPNetwork from werkzeug.routing import BaseConverter +from b3desk.models import db + def secret_key(): return current_app.config["SECRET_KEY"] diff --git a/web/migrations/alembic_helpers.py b/web/migrations/alembic_helpers.py index 2c1b5266..8f510911 100644 --- a/web/migrations/alembic_helpers.py +++ b/web/migrations/alembic_helpers.py @@ -1,7 +1,7 @@ # Code based on https://github.com/talkpython/data-driven-web-apps-with-flask from alembic import op -from sqlalchemy import engine_from_config from sqlalchemy import MetaData +from sqlalchemy import engine_from_config def load_schema(): diff --git a/web/migrations/versions/1094e771bd3f_create_meeting_files_table.py b/web/migrations/versions/1094e771bd3f_create_meeting_files_table.py index a38376f2..bd7c607a 100644 --- a/web/migrations/versions/1094e771bd3f_create_meeting_files_table.py +++ b/web/migrations/versions/1094e771bd3f_create_meeting_files_table.py @@ -4,6 +4,7 @@ Revises: 8fe077ecfb10 Create Date: 2023-02-28 14:30:43.642893 """ + import os import sys @@ -16,7 +17,6 @@ import alembic_helpers - # revision identifiers, used by Alembic. revision = "1094e771bd3f" down_revision = "8fe077ecfb10" diff --git a/web/migrations/versions/54f71a7705a8_initial_tables2.py b/web/migrations/versions/54f71a7705a8_initial_tables2.py index 391dd6d3..979ab6cb 100644 --- a/web/migrations/versions/54f71a7705a8_initial_tables2.py +++ b/web/migrations/versions/54f71a7705a8_initial_tables2.py @@ -4,6 +4,7 @@ Revises: Create Date: 2023-01-03 18:01:03.770238 """ + import os import sys diff --git a/web/migrations/versions/65acbe9b0646_add_user_nextcloud_connection_info.py b/web/migrations/versions/65acbe9b0646_add_user_nextcloud_connection_info.py index b07622c1..df6c8065 100644 --- a/web/migrations/versions/65acbe9b0646_add_user_nextcloud_connection_info.py +++ b/web/migrations/versions/65acbe9b0646_add_user_nextcloud_connection_info.py @@ -4,6 +4,7 @@ Revises: 1094e771bd3f Create Date: 2023-02-28 14:35:21.691915 """ + import os import sys @@ -16,7 +17,6 @@ import alembic_helpers - # revision identifiers, used by Alembic. revision = "65acbe9b0646" down_revision = "1094e771bd3f" diff --git a/web/migrations/versions/7d80b9223a1e_guestpolicy_migration.py b/web/migrations/versions/7d80b9223a1e_guestpolicy_migration.py index 9748c37d..2c9467d4 100644 --- a/web/migrations/versions/7d80b9223a1e_guestpolicy_migration.py +++ b/web/migrations/versions/7d80b9223a1e_guestpolicy_migration.py @@ -4,6 +4,7 @@ Revises: 54f71a7705a8 Create Date: 2023-02-28 14:29:28.456201 """ + import os import sys @@ -16,7 +17,6 @@ import alembic_helpers - # revision identifiers, used by Alembic. revision = "7d80b9223a1e" down_revision = "54f71a7705a8" diff --git a/web/migrations/versions/8fe077ecfb10_add_last_connection_utc_datetime.py b/web/migrations/versions/8fe077ecfb10_add_last_connection_utc_datetime.py index 04bfd63a..08fa9abb 100644 --- a/web/migrations/versions/8fe077ecfb10_add_last_connection_utc_datetime.py +++ b/web/migrations/versions/8fe077ecfb10_add_last_connection_utc_datetime.py @@ -4,6 +4,7 @@ Revises: 9aac3b5e1582 Create Date: 2022-08-12 09:09:47.674373 """ + import os import sys @@ -16,7 +17,6 @@ import alembic_helpers - # revision identifiers, used by Alembic. revision = "8fe077ecfb10" down_revision = "9aac3b5e1582" diff --git a/web/migrations/versions/9aac3b5e1582_welcome_message_unbound.py b/web/migrations/versions/9aac3b5e1582_welcome_message_unbound.py index aeae1bb8..2292b2a3 100644 --- a/web/migrations/versions/9aac3b5e1582_welcome_message_unbound.py +++ b/web/migrations/versions/9aac3b5e1582_welcome_message_unbound.py @@ -4,6 +4,7 @@ Revises: 7d80b9223a1e Create Date: 2021-05-06 17:44:17.835474 """ + import os import sys diff --git a/web/migrations/versions/9d4ba8cf710a_remove_obsolete_meeting_attributes.py b/web/migrations/versions/9d4ba8cf710a_remove_obsolete_meeting_attributes.py index ad0cc2d2..ccdc4f30 100644 --- a/web/migrations/versions/9d4ba8cf710a_remove_obsolete_meeting_attributes.py +++ b/web/migrations/versions/9d4ba8cf710a_remove_obsolete_meeting_attributes.py @@ -4,10 +4,10 @@ Revises: 65acbe9b0646 Create Date: 2022-08-12 15:43:01.721995 """ + import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "9d4ba8cf710a" down_revision = "65acbe9b0646" diff --git a/web/misc/gunicorn.py b/web/misc/gunicorn.py index 89f4b8f0..658c5b05 100644 --- a/web/misc/gunicorn.py +++ b/web/misc/gunicorn.py @@ -202,8 +202,8 @@ def when_ready(server): def worker_int(worker): worker.log.info("worker received INT or QUIT signal") ## get traceback info - import threading import sys + import threading import traceback id2name = {th.ident: th.name for th in threading.enumerate()} diff --git a/web/tests/conftest.py b/web/tests/conftest.py index 5753c6ba..692b3a23 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -4,15 +4,16 @@ import wsgiref from pathlib import Path -import b3desk.utils import portpicker import pytest -from b3desk import create_app from flask_migrate import Migrate from flask_webtest import TestApp from wsgidav.fs_dav_provider import FilesystemProvider from wsgidav.wsgidav_app import WsgiDAVApp +import b3desk.utils +from b3desk import create_app + b3desk.utils.secret_key = lambda: "AZERTY" from b3desk.models import db diff --git a/web/tests/meeting/test_meeting.py b/web/tests/meeting/test_meeting.py index 73cc5a4b..f4ccbbcb 100644 --- a/web/tests/meeting/test_meeting.py +++ b/web/tests/meeting/test_meeting.py @@ -3,9 +3,10 @@ from urllib.parse import urlparse import pytest + from b3desk.models import db -from b3desk.models.meetings import Meeting from b3desk.models.meetings import MODERATOR_ONLY_MESSAGE_MAXLENGTH +from b3desk.models.meetings import Meeting @pytest.fixture() diff --git a/web/tests/test_authentication.py b/web/tests/test_authentication.py index d66031f0..0d03eecc 100644 --- a/web/tests/test_authentication.py +++ b/web/tests/test_authentication.py @@ -1,4 +1,5 @@ import requests + from b3desk.models import db from b3desk.models.users import User diff --git a/web/tests/test_user.py b/web/tests/test_user.py index a464e579..944f7aca 100644 --- a/web/tests/test_user.py +++ b/web/tests/test_user.py @@ -1,11 +1,12 @@ from datetime import date import pytest +from freezegun import freeze_time + from b3desk.models import db +from b3desk.models.users import User from b3desk.models.users import get_or_create_user from b3desk.models.users import make_nextcloud_credentials_request -from b3desk.models.users import User -from freezegun import freeze_time def test_get_or_create_user(client_app): From 664151655c90f4d5a925c2e8d7b181a128353494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 13 Mar 2024 10:19:50 +0100 Subject: [PATCH 178/213] chore: refactor flask-migrate initialization --- web/b3desk/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index 13f95d99..f860dbf5 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -36,6 +36,7 @@ cache = Cache() csrf = CSRFProtect() auth = OIDCAuthentication({"default": None, "attendee": None}) +migrate = Migrate() def setup_configuration(app, config=None): @@ -104,7 +105,7 @@ def setup_database(app): from .models import db db.init_app(app) - Migrate(app, db, compare_type=True) + migrate.init_app(app, db, compare_type=True) def setup_jinja(app): From 7b642bb2d9bb7fd76d8fb1cef85ce9a36b6b07b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 24 Jan 2024 18:03:16 +0100 Subject: [PATCH 179/213] perf: cache BBB API calls --- web/b3desk/models/bbb.py | 27 ++++ web/b3desk/settings.py | 4 + web/tests/conftest.py | 1 + web/tests/test_bbb_api_caching.py | 198 ++++++++++++++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 web/tests/test_bbb_api_caching.py diff --git a/web/b3desk/models/bbb.py b/web/b3desk/models/bbb.py index 9db7fd4a..9b96c283 100644 --- a/web/b3desk/models/bbb.py +++ b/web/b3desk/models/bbb.py @@ -11,6 +11,7 @@ import hashlib from datetime import datetime from datetime import timezone +from urllib.parse import urlparse from xml.etree import ElementTree import requests @@ -20,6 +21,25 @@ from b3desk.tasks import background_upload +from .. import cache + + +def cache_key(func, caller, prepped, *args, **kwargs): + return prepped.url + + +def caching_exclusion(func, caller, prepped, *args, **kwargs): + """Only read-only methods should be cached.""" + url = urlparse(prepped.url) + endpoint_name = url.path.split("/")[-1] + return prepped.method != "GET" or endpoint_name not in ( + "isMeetingRunning", + "getMeetingInfo", + "getMeetings", + "getRecordings", + "getRecordingTextTracks", + ) + class BBB: """Interface to BBB API.""" @@ -45,12 +65,18 @@ def bbb_request(self, action, method="GET", **kwargs): prepped.prepare_url(prepped.url, params={"checksum": checksum}) return prepped + @cache.memoize( + unless=caching_exclusion, + timeout=current_app.config["BIGBLUEBUTTON_API_CACHE_DURATION"], + ) def bbb_response(self, request): session = requests.Session() current_app.logger.debug("BBB API request %s: %s", request.method, request.url) response = session.send(request) return {c.tag: c.text for c in ElementTree.fromstring(response.content)} + bbb_response.make_cache_key = cache_key + def is_meeting_running(self): """https://docs.bigbluebutton.org/development/api/#ismeetingrunning""" request = self.bbb_request( @@ -257,6 +283,7 @@ def get_meeting_info(self): ) return self.bbb_response(request) + @cache.memoize(timeout=current_app.config["BIGBLUEBUTTON_API_CACHE_DURATION"]) def get_recordings(self): """https://docs.bigbluebutton.org/development/api/#getrecordings""" request = self.bbb_request( diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index e238284f..9f72cdde 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -1016,3 +1016,7 @@ def get_rie_network_ips( MATOMO_SITE_ID: Optional[str] = None """ID de l’instance B3Desk dans Matomo.""" + + BIGBLUEBUTTON_API_CACHE_DURATION: int = 5 + """Le temps de mise en cache (en secondes) des réponses aux requêtes GET à + l'API BBB.""" diff --git a/web/tests/conftest.py b/web/tests/conftest.py index 692b3a23..f4673546 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -95,6 +95,7 @@ def configuration(tmp_path, iam_server, iam_client, smtpd): "CACHE_TYPE": "SimpleCache", # Disable cache in unit tests "CACHE_DEFAULT_TIMEOUT": 0, + "BIGBLUEBUTTON_API_CACHE_DURATION": 0, "MEETING_LOGOUT_URL": "https://example.org/logout", "MAIL_MEETING": True, "SMTP_FROM": "from@example.org", diff --git a/web/tests/test_bbb_api_caching.py b/web/tests/test_bbb_api_caching.py new file mode 100644 index 00000000..0947ea78 --- /dev/null +++ b/web/tests/test_bbb_api_caching.py @@ -0,0 +1,198 @@ +import pytest + + +@pytest.fixture +def configuration(configuration): + configuration["BIGBLUEBUTTON_API_CACHE_DURATION"] = 5 + return configuration + + +IS_MEETING_RUNNING_SUCCESS_RESPONSE = """ + + SUCCESS + true + +""" + + +def test_is_meeting_running(meeting, mocker): + """Tests that the requests to the ismeetingrunning endpoint of the BBB API + are cached.""" + + class Response: + content = IS_MEETING_RUNNING_SUCCESS_RESPONSE + + send = mocker.patch("requests.Session.send", return_value=Response) + + assert send.call_count == 0 + + assert meeting.bbb.is_meeting_running() + assert send.call_count == 1 + + assert meeting.bbb.is_meeting_running() + assert send.call_count == 1 + + +GET_RECORDINGS_RESPONSE = """ + + SUCCESS + + + ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + Fred's Room + false + true + published + 1530718721124 + 1530718810456 + 3 + 951067 + + https://bbb-analytics.url + false + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + Fred's Room + + + unknown + 0 + false + + 1104836 + + + presentation + https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + 7177 + 0 + 1104836 + + + Welcome tohttps://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-1.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-2.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-3.png + + + + + video + https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/meeting.mp4 + 0 + 0 + 1104836 + + + + + ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + Fred's Room + false + true + published + 1530278898111 + 1530281194326 + 7 + 381530 + + Recording title hand written + Fred's Room + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + https://bbb-analytics.url + false + + + unknown + 0 + false + + + + podcast + https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/audio.ogg + 0 + 33 + + + presentation + https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + 139458 + 33 + + + Welcome tohttps://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-1.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-2.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-3.png + + + + + + + +""" + + +def test_get_recordings(meeting, mocker): + """Tests that the requests to the getrecordings endpoint of the BBB API are + cached.""" + + class Response: + content = GET_RECORDINGS_RESPONSE + + send = mocker.patch("requests.Session.send", return_value=Response) + + assert send.call_count == 0 + + recordings = meeting.bbb.get_recordings() + assert len(recordings) == 2 + assert send.call_count == 1 + + recordings = meeting.bbb.get_recordings() + assert len(recordings) == 2 + assert send.call_count == 1 + + +CREATE_RESPONSE = """ + + SUCCESS + Test + 640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613 + bbb-none + ap + mp + 1531155809613 + 70757 + 613-555-1234 + Mon Jul 09 17:03:29 UTC 2018 + false + 0 + false + duplicateWarning + This conference was already in existence and may currently be in progress. + +""" + + +def test_create(meeting, mocker): + """Tests that the requests to the create endpoint of the BBB API are NOT + cached.""" + + class Response: + content = CREATE_RESPONSE + + send = mocker.patch("requests.Session.send", return_value=Response) + mocker.patch("requests.post") + + assert send.call_count == 0 + + data = meeting.bbb.create() + assert data["returncode"] == "SUCCESS" + assert send.call_count == 1 + + data = meeting.bbb.create() + assert data["returncode"] == "SUCCESS" + assert send.call_count == 2 From df1ae34d80524357407ea6397c56013ccd90a7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Thu, 14 Mar 2024 09:43:08 +0100 Subject: [PATCH 180/213] chore: dependencies update --- poetry.lock | 122 ++++++++++----------------------------- pyproject.toml | 14 +---- web/requirements.app.txt | 2 +- web/requirements.dev.txt | 13 ++--- web/requirements.doc.txt | 2 +- 5 files changed, 39 insertions(+), 114 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96917b39..d96e2a63 100644 --- a/poetry.lock +++ b/poetry.lock @@ -233,52 +233,6 @@ files = [ {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, ] -[[package]] -name = "black" -version = "23.12.1" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "blinker" version = "1.7.0" @@ -822,13 +776,13 @@ test = ["pytest (>=6)"] [[package]] name = "faker" -version = "21.0.1" +version = "24.2.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-21.0.1-py3-none-any.whl", hash = "sha256:0afc67ec898a2d71842a3456e9302620ebc35fab6ad4f3829693fdf151fa4a3a"}, - {file = "Faker-21.0.1.tar.gz", hash = "sha256:bb404bba449b87e6b54a8c50b4602765e9c1a42eaf48abfceb025e42fed01608"}, + {file = "Faker-24.2.0-py3-none-any.whl", hash = "sha256:dce4754921f9fa7e2003c26834093361b8f45072e0f46f172d6ca1234774ecd4"}, + {file = "Faker-24.2.0.tar.gz", hash = "sha256:87d5e7730426e7b36817921679c4eaf3d810cedb8c81194f47adc3df2122ca18"}, ] [package.dependencies] @@ -1554,17 +1508,6 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "myst-parser" version = "2.0.0" @@ -1593,15 +1536,18 @@ testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4, [[package]] name = "netaddr" -version = "0.10.1" +version = "1.2.1" description = "A network address manipulation library for Python" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "netaddr-0.10.1-py2.py3-none-any.whl", hash = "sha256:9822305b42ea1020d54fee322d43cee5622b044c07a1f0130b459bb467efcf88"}, - {file = "netaddr-0.10.1.tar.gz", hash = "sha256:f4da4222ca8c3f43c8e18a8263e5426c750a3a837fdfeccf74c68d0408eaa3bf"}, + {file = "netaddr-1.2.1-py3-none-any.whl", hash = "sha256:bd9e9534b0d46af328cf64f0e5a23a5a43fca292df221c85580b27394793496e"}, + {file = "netaddr-1.2.1.tar.gz", hash = "sha256:6eb8fedf0412c6d294d06885c110de945cf4d22d2b510d0404f4e06950857987"}, ] +[package.extras] +nicer-shell = ["ipython"] + [[package]] name = "nodeenv" version = "1.8.0" @@ -1656,17 +1602,6 @@ files = [ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - [[package]] name = "platformdirs" version = "4.2.0" @@ -2047,13 +1982,13 @@ test = ["pytest", "pytest-cov", "requests", "webob", "webtest"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -2061,11 +1996,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -2102,20 +2037,20 @@ python-dotenv = ">=0.9.1" [[package]] name = "pytest-iam" -version = "0.0.7" +version = "0.0.8" description = "A fully functional OAUTH2 / OpenID Connect (OIDC) server to be used in your testsuite" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "pytest_iam-0.0.7-py3-none-any.whl", hash = "sha256:88e2eabfea2a0be100d9034006ba89229f40bd9c9fdd02a06d26d24e54d3f25f"}, - {file = "pytest_iam-0.0.7.tar.gz", hash = "sha256:4fed93ad7196794305f2d295ddc03f32650ad270683f49292ba183823ba7fab2"}, + {file = "pytest_iam-0.0.8-py3-none-any.whl", hash = "sha256:20c16b21bbe23018a3cbc1cd9412586387a388f39a04f365b986fddd05f98148"}, + {file = "pytest_iam-0.0.8.tar.gz", hash = "sha256:39ee394836fba85b27a2e93c80ee87aba01ff9cbb95c12f84bc125ecd1b1b931"}, ] [package.dependencies] canaille = {version = "<1", extras = ["oidc"]} -faker = ">=21.0.0,<22.0.0" -portpicker = ">=1.6.0,<2.0.0" -pytest = ">=7.0.0,<8.0.0" +faker = ">=21.0.0" +portpicker = ">=1.6.0" +pytest = ">=7.0.0" [[package]] name = "pytest-mock" @@ -2216,6 +2151,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2291,18 +2227,18 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "setuptools" -version = "69.1.1" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, - {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2888,4 +2824,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0" -content-hash = "bcd25ed05168e4695c2b6a05ec7dd5a8587935fe959d126c85e8f1e9a1654988" +content-hash = "b20b5c0eb60d094abb4063245fc5873abbc2714082a157d1a9a629e547261b24" diff --git a/pyproject.toml b/pyproject.toml index cdf67060..dc93cbb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ flask-pyoidc = "^3.14.2" flask-sqlalchemy = "^3.0.3" flask-wtf = "^1.2.1" gunicorn = "21.2.0" -netaddr = "^0.10.0" +netaddr = "^1.2.1" psycopg2 = "^2.9.3" pydantic-settings = "^2.1.0" redis = "^5.0.1" @@ -36,16 +36,15 @@ webdavclient3 = "3.14.6" optional = true [tool.poetry.group.dev.dependencies] -black = "^23.1.0" coverage = "^7.2.2" flake8 = "^7.0.0" Flask-WebTest = "^0.1.3" freezegun = "^1.2.2" pre-commit = "^3.1.1" -pytest = "^7.4.0" +pytest = "^8.0.0" pytest-cov = "^4.1.0" pytest-dotenv = "^0.5.2" -pytest-iam = "^0.0.7" +pytest-iam = "^0.0.8" pytest-mock = "^3.11.1" wsgidav = "^4.3.0" pytest-smtpd = "^0.1.0" @@ -61,13 +60,6 @@ sphinx = "^7.0.0" sphinx-rtd-theme = "^2.0.0" sphinx-issues = "^3.0.0" -[tool.black] -extend-exclude = ''' -( - postgres/data -) -''' - [tool.pytest.ini_options] testpaths = "web" markers = [ diff --git a/web/requirements.app.txt b/web/requirements.app.txt index 55097f06..79548454 100644 --- a/web/requirements.app.txt +++ b/web/requirements.app.txt @@ -40,7 +40,7 @@ kombu==5.3.5 ; python_full_version >= "3.8.1" and python_version < "4.0" lxml==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" mako==1.3.2 ; python_full_version >= "3.8.1" and python_version < "4.0" markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" -netaddr==0.10.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +netaddr==1.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" oic==1.6.1 ; python_full_version >= "3.8.1" and python_version < "4.0" packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" prompt-toolkit==3.0.43 ; python_full_version >= "3.8.1" and python_version < "4.0" diff --git a/web/requirements.dev.txt b/web/requirements.dev.txt index 30b6f713..3edee862 100644 --- a/web/requirements.dev.txt +++ b/web/requirements.dev.txt @@ -3,7 +3,6 @@ atpublic==4.0 ; python_full_version >= "3.8.1" and python_version < "4" attrs==23.2.0 ; python_full_version >= "3.8.1" and python_version < "4" authlib==1.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" beautifulsoup4==4.12.3 ; python_full_version >= "3.8.1" and python_version < "4" -black==23.12.1 ; python_full_version >= "3.8.1" and python_version < "4.0" blinker==1.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" canaille[oidc]==0.0.42 ; python_full_version >= "3.8.1" and python_version < "4.0" cffi==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4" and platform_python_implementation != "PyPy" @@ -17,7 +16,7 @@ cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_version < "4.0" distlib==0.3.8 ; python_full_version >= "3.8.1" and python_version < "4.0" exceptiongroup==1.2.0 ; python_full_version >= "3.8.1" and python_version < "3.11" -faker==21.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +faker==24.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" filelock==3.13.1 ; python_full_version >= "3.8.1" and python_version < "4.0" flake8==7.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" flask-webtest==0.1.4 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -33,10 +32,8 @@ json5==0.9.22 ; python_full_version >= "3.8.1" and python_version < "4.0" lxml==5.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" nodeenv==1.8.0 ; python_full_version >= "3.8.1" and python_version < "4.0" packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -pathspec==0.12.1 ; python_full_version >= "3.8.1" and python_version < "4.0" platformdirs==4.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pluggy==1.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" portpicker==1.6.0 ; python_full_version >= "3.8.1" and python_version < "4.0" @@ -48,19 +45,19 @@ pyflakes==3.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pyquery==2.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-cov==4.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-dotenv==0.5.2 ; python_full_version >= "3.8.1" and python_version < "4.0" -pytest-iam==0.0.7 ; python_full_version >= "3.8.1" and python_version < "4.0" +pytest-iam==0.0.8 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-mock==3.12.0 ; python_full_version >= "3.8.1" and python_version < "4.0" pytest-smtpd==0.1.0 ; python_full_version >= "3.8.1" and python_version < "4" -pytest==7.4.4 ; python_full_version >= "3.8.1" and python_version < "4.0" +pytest==8.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" python-dateutil==2.9.0.post0 ; python_full_version >= "3.8.1" and python_version < "4.0" python-dotenv==1.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_version < "4.0" -setuptools==69.1.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +setuptools==69.2.0 ; python_full_version >= "3.8.1" and python_version < "4.0" six==1.16.0 ; python_full_version >= "3.8.1" and python_version < "4.0" smtpdfix==0.5.1 ; python_full_version >= "3.8.1" and python_version < "4" soupsieve==2.5 ; python_full_version >= "3.8.1" and python_version < "4" tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version <= "3.11.0a6" -typing-extensions==4.10.0 ; python_full_version >= "3.8.1" and python_version < "3.11" +typing-extensions==4.10.0 ; python_full_version >= "3.8.1" and python_version <= "3.8" virtualenv==20.25.1 ; python_full_version >= "3.8.1" and python_version < "4.0" waitress==3.0.0 ; python_full_version >= "3.8.1" and python_version < "4" webob==1.8.7 ; python_full_version >= "3.8.1" and python_version < "4" diff --git a/web/requirements.doc.txt b/web/requirements.doc.txt index c7706be6..7bd2e6a5 100644 --- a/web/requirements.doc.txt +++ b/web/requirements.doc.txt @@ -48,7 +48,7 @@ markupsafe==2.1.5 ; python_full_version >= "3.8.1" and python_version < "4.0" mdit-py-plugins==0.4.0 ; python_full_version >= "3.8.1" and python_version < "4.0" mdurl==0.1.2 ; python_full_version >= "3.8.1" and python_version < "4.0" myst-parser==2.0.0 ; python_full_version >= "3.8.1" and python_version < "4.0" -netaddr==0.10.1 ; python_full_version >= "3.8.1" and python_version < "4.0" +netaddr==1.2.1 ; python_full_version >= "3.8.1" and python_version < "4.0" oic==1.6.1 ; python_full_version >= "3.8.1" and python_version < "4.0" packaging==24.0 ; python_full_version >= "3.8.1" and python_version < "4.0" prompt-toolkit==3.0.43 ; python_full_version >= "3.8.1" and python_version < "4.0" From 5527bfba7c52d3679650707a41f326d90a056d38 Mon Sep 17 00:00:00 2001 From: Loan Robert Date: Thu, 14 Mar 2024 11:43:11 +0100 Subject: [PATCH 181/213] Update versioning documentation with version number update and recommended git tag management --- documentation/maintainers/versioning.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/documentation/maintainers/versioning.md b/documentation/maintainers/versioning.md index 5b881e35..3e5f7f2c 100644 --- a/documentation/maintainers/versioning.md +++ b/documentation/maintainers/versioning.md @@ -1,7 +1,12 @@ # Publication de nouvelles versions +La branche de référence pour les releases est `production`. C'est ici qu'on retrouve les différentes versions installées sur les instances. À ces releases correspondent des tags git. + +Pour publier une nouvelle version : +- S'assurer d'être sur la branche `production` +- Mettre à jour le numéro de version dans `pyproject.toml` et dans `web/flaskr/__init__.py` - Mettre un tag sur le commit, portant le numéro de la version, avec `git tag vX.Y.Z` -- Pousser le commit ET le tag `git push origin main --tags` +- Pousser le commit ET le tag `git push origin production --follow-tags` - Se rendre sur [la page github de publication de version](https://github.com/numerique-gouv/b3desk/releases/new) - Choisir le tag récemment ajouté, remplir les informations, publier la version. From 1874d75d825dd82dd9b6422dab393be1d4dac456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 15 Mar 2024 15:26:17 +0100 Subject: [PATCH 182/213] chore: display a warning badge for development versions --- pyproject.toml | 2 +- web/b3desk/__init__.py | 2 +- web/b3desk/templates/footer.html | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dc93cbb6..f7d5c744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "b3desk" -version = "1.1.3" +version = "dev" description = "Outil de visioconférence pour les agents de l'Education Nationale et de l'Etat en général." authors = ["Your Name "] readme = "README.md" diff --git a/web/b3desk/__init__.py b/web/b3desk/__init__.py index f860dbf5..ca72e670 100644 --- a/web/b3desk/__init__.py +++ b/web/b3desk/__init__.py @@ -28,7 +28,7 @@ from .utils import model_converter -__version__ = "1.1.3" +__version__ = "dev" LANGUAGES = ["en", "fr"] diff --git a/web/b3desk/templates/footer.html b/web/b3desk/templates/footer.html index f7d04bc8..57320887 100644 --- a/web/b3desk/templates/footer.html +++ b/web/b3desk/templates/footer.html @@ -25,7 +25,13 @@