first comit
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.idea/
|
||||
256
README.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# TP : Galerie d'images (flask)
|
||||
|
||||
## Informations générales
|
||||
|
||||
**Cours** : Python Avancé > Web > Flask \
|
||||
**Objectifs pédagogiques** :
|
||||
- Python avancé : framework Flask pour le développement web
|
||||
- Outils modernes (poetry, PyCharm)
|
||||
- Bonnes pratiques de l'entreprise
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
### Installation et configuration de l’environnement
|
||||
|
||||
Installer les dépendances avec `poetry install` depuis un terminal PyCharm.
|
||||
|
||||
### Connaissances préalables
|
||||
|
||||
- Connaissances de base en programmation
|
||||
|
||||
---
|
||||
|
||||
## Énoncé
|
||||
|
||||
### Contexte
|
||||
|
||||
L'objectif de ce TP Bonus est de développer une application web simple mais robuste avec le micro-framework Flask.
|
||||
|
||||
Vous êtes invité à lire les premières sections de la [documentation officielle de Flask](https://flask.palletsprojects.com/en/stable/quickstart/) pour vous familiariser avec les concepts de base, avant de commencer le TP.
|
||||
|
||||
Cette application web affichera une galerie d'images à partir de fichiers stockés localement sur le serveur.
|
||||
|
||||
Les images étant fournies par le serveur avec différentes largeurs, cela permettra au client d'optimiser la performance réseau en fonction de la taille de l'image réelle à afficher côté client (navigateur).
|
||||
|
||||
Pour vous concentrer sur la logique métier et le fonctionnement de Flask, une partie significative du code vous est déjà fournie :
|
||||
|
||||
* Les **templates HTML** (`layout.html`, `gallery.html`, `404.html`) qui gèrent l'affichage.
|
||||
* Les **fichiers statiques** (CSS Bootstrap) pour le design, ainsi que les images statiques.
|
||||
* Un module utilitaire `utils.py` contenant une fonction `build_image_structure` qui analyse et structure les données du répertoire contenant les images.
|
||||
* Une suite de **tests automatisés** (`test_app.py`) qui vous permettra de valider votre travail au fur et à mesure.
|
||||
|
||||
Votre mission est de compléter le fichier `src/app/views.py` pour faire le lien entre les requêtes des utilisateurs, les données des images et les templates HTML.
|
||||
|
||||
### Objectifs pédagogiques
|
||||
|
||||
À la fin de ce TP, vous saurez :
|
||||
|
||||
* Créer et organiser des routes (vues) dans une application Flask à l'aide de Blueprints.
|
||||
* Faire le rendu de templates HTML en leur transmettant des données dynamiques (`render_template`).
|
||||
* Gérer les erreurs de manière élégante (`try...except`) pour rendre une application plus robuste.
|
||||
* Créer un premier endpoint d'API simple qui retourne des données au format JSON.
|
||||
* Utiliser une suite de tests (Pytest) pour guider et valider votre développement (approche TDD-lite).
|
||||
* Lancer et déboguer une application Flask localement.
|
||||
|
||||
### Mise en place
|
||||
|
||||
Le projet utilise `poetry` pour la gestion des dépendances.
|
||||
|
||||
La structure du projet est la suivante :
|
||||
|
||||
```
|
||||
.
|
||||
├── poetry.lock
|
||||
├── pyproject.toml
|
||||
├── src
|
||||
│ └── app
|
||||
│ ├── __init__.py # Point d'entrée de l'application
|
||||
│ ├── static/ # Fichiers CSS, et images
|
||||
│ ├── templates/ # Fichiers HTML (templates Jinja2)
|
||||
│ ├── utils.py # Fonctions utilitaires (fourni)
|
||||
│ └── views.py # Le fichier à compléter !
|
||||
└── tests
|
||||
└── test_views.py # Tests pour valider votre code (fourni)
|
||||
```
|
||||
|
||||
### Travail à réaliser
|
||||
|
||||
Ouvrez le fichier `src/app/views.py`. Il est actuellement presque vide. Vous allez le compléter étape par étape.
|
||||
|
||||
#### Étape 1 : afficher la galerie d'images
|
||||
|
||||
Votre premier objectif est de créer la vue principale qui affiche la galerie.
|
||||
|
||||
**Tâche :** Créez une fonction `index()` qui répond à la route `/` (la racine du site).
|
||||
|
||||
**Logique à implémenter dans la fonction `index()` :**
|
||||
|
||||
1. Construisez le chemin vers le dossier contenant les images. Ce dossier se trouve dans `static/images`. Utilisez `current_app.static_folder` de Flask et la bibliothèque `pathlib` pour un code propre et portable.
|
||||
|
||||
2. Appelez la fonction `build_image_structure()` (importée depuis `.utils`) en lui passant le chemin du dossier des images.
|
||||
|
||||
3. **Rendez votre code robuste !** Encadrez l'appel à cette fonction dans un bloc `try...except`. En cas d'erreur (par exemple, si le dossier n'existe pas ou si un autre problème survient), vous devez éviter que l'application ne plante.
|
||||
|
||||
* En cas de succès, vous passez les données des images au template.
|
||||
|
||||
* Si aucune image n'est trouvée par la fonction, prévoyez un message d'erreur à afficher.
|
||||
|
||||
* En cas d'exception, logguez l'erreur et préparez un message d'erreur générique pour l'utilisateur.
|
||||
|
||||
4. Faites le rendu du template `gallery.html` en utilisant `render_template()`. Vous devez lui passer deux variables :
|
||||
|
||||
* `structured_images`: Le dictionnaire d'images retourné par `build_image_structure`.
|
||||
|
||||
* `error_message`: Un message d'erreur (ou `None` si tout va bien).
|
||||
|
||||
**Diagramme de flux (Étape 1) :**
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Client[Navigateur]
|
||||
A[Utilisateur]
|
||||
end
|
||||
subgraph Serveur[Serveur Flask]
|
||||
B["Route /"]
|
||||
C["views.py: index()"]
|
||||
D["utils.py: build_image_structure()"]
|
||||
E["templates/gallery.html"]
|
||||
F[Réponse HTML]
|
||||
end
|
||||
subgraph SystemeFichiers[Disque Serveur]
|
||||
G["static/images"]
|
||||
end
|
||||
|
||||
A -- "1. Requête GET /" --> B
|
||||
B -- "2. Déclenche" --> C
|
||||
C -- "3. Construit chemin et appelle" --> D
|
||||
D -- "4. Lit le dossier" --> G
|
||||
G -- "5. Retourne la structure" --> D
|
||||
D -- "6. Retourne dict_images" --> C
|
||||
C -- "7. Injecte données dans" --> E
|
||||
E -- "8. Génère" --> F
|
||||
F -- "9. Envoie" --> A
|
||||
```
|
||||
|
||||
**Logique de gestion d'erreur (commune aux étapes 1 et 4) :**
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Début de la vue] --> B{try}
|
||||
B -- "Succès" --> C["Appel: build_image_structure()"]
|
||||
C --> D{Images trouvées?}
|
||||
D -- "Oui" --> E[Préparer réponse 200 OK - HTML ou JSON]
|
||||
D -- "Non" --> F[Préparer réponse - HTML avec erreur ou JSON 404]
|
||||
B -- "Échec" --> G[except Exception as e]
|
||||
G --> H[Logguer l'erreur e]
|
||||
H --> I[Préparer réponse - HTML avec erreur ou JSON 500]
|
||||
E --> Z[Fin]
|
||||
F --> Z[Fin]
|
||||
I --> Z[Fin]
|
||||
```
|
||||
|
||||
#### Étape 2 : valider avec les tests
|
||||
|
||||
Avant même de regarder le résultat dans votre navigateur, vérifiez que votre code est correct en lançant la suite de tests.
|
||||
|
||||
```bash
|
||||
poetry run pytest -p no:warnings
|
||||
```
|
||||
|
||||
Analysez le résultat. Certains tests qui échouaient au début devraient maintenant passer. L'objectif est de faire passer tous les tests relatifs à la galerie.
|
||||
|
||||
#### Étape 3 : lancer le serveur et constater le résultat
|
||||
|
||||
Une fois les tests au vert, lancez le serveur de développement Flask.
|
||||
|
||||
1. Assurez-vous d'être dans le bon dossier :
|
||||
|
||||
```bash
|
||||
cd src
|
||||
```
|
||||
|
||||
2. Lancez le serveur en mode "debug" (ce mode recharge automatiquement le serveur à chaque modification de votre code) :
|
||||
|
||||
```bash
|
||||
flask run --debug
|
||||
```
|
||||
|
||||
3. Ouvrez votre navigateur à l'adresse [http://127.0.0.1:5000](http://127.0.0.1:5000) et admirez votre galerie !
|
||||
|
||||
#### Étape 4 : créer un endpoint d'API JSON
|
||||
|
||||
Votre second objectif est de fournir les mêmes données via une API, au format JSON.
|
||||
|
||||
**Tâche :** Créez une fonction `api_images()` qui répond à la route `/api/images`.
|
||||
|
||||
**Logique à implémenter :**
|
||||
|
||||
1. La logique de récupération des données est la même que pour la vue `index()` : chemin du dossier, appel à `build_image_structure()`, gestion des exceptions.
|
||||
|
||||
2. La différence réside dans la réponse :
|
||||
|
||||
* En cas de succès, retournez directement le dictionnaire `structured_images`. Flask le convertira automatiquement en une réponse JSON avec un code de statut `200 OK`.
|
||||
|
||||
* Si aucune image n'est trouvée, retournez un dictionnaire d'erreur (ex: `{'error': "Aucune image n'a été trouvée"}`) et le code de statut `404 Not Found`.
|
||||
|
||||
* En cas d'exception serveur, retournez un dictionnaire d'erreur (ex: `{'error': "Erreur interne du serveur"}`) et le code de statut `500 Internal Server Error`.
|
||||
|
||||
**Diagramme de flux (Étape 4) :**
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Client[Client API ex: Postman, script]
|
||||
A[Utilisateur/Service]
|
||||
end
|
||||
subgraph Serveur[Serveur Flask]
|
||||
B[Route /api/images]
|
||||
C["views.py: api_images()"]
|
||||
D["utils.py: build_image_structure()"]
|
||||
E[Réponse JSON]
|
||||
end
|
||||
subgraph SystemeFichiers[Disque Serveur]
|
||||
G["static/images"]
|
||||
end
|
||||
|
||||
A -- "1. Requête GET /api/images" --> B
|
||||
B -- "2. Déclenche" --> C
|
||||
C -- "3. Construit chemin et appelle" --> D
|
||||
D -- "4. Lit le dossier" --> G
|
||||
G -- "5. Retourne la structure" --> D
|
||||
D -- "6. Retourne dict_images" --> C
|
||||
C -- "7. Formate en JSON" --> E
|
||||
E -- "8. Envoie" --> A
|
||||
```
|
||||
|
||||
#### Étape 5 : ajouter un ou plusieurs tests pour l'API
|
||||
|
||||
Pour aller plus loin, ouvrez le fichier `tests/test_app.py` et ajoutez un ou plusieurs nouveaux tests pour votre API.
|
||||
|
||||
**Idée de test :**
|
||||
Créez une fonction `test_api_images_success()` qui :
|
||||
|
||||
1. Fait un `GET` sur `/api/images`.
|
||||
|
||||
2. Vérifie que le code de statut est bien `200`.
|
||||
|
||||
3. Vérifie que le `content_type` de la réponse est bien `application/json`.
|
||||
|
||||
4. Vérifie que la réponse JSON contient des clés attendues (par exemple, le nom d'une des images).
|
||||
|
||||
Relancez les tests pour vous assurer que l'ancien et le nouveau code fonctionnent parfaitement.
|
||||
|
||||
### Bonnes pratiques
|
||||
|
||||
* La page d'accueil (`/`) s'affiche correctement avec les images. Les images téléchargées par le navigateur sont cohérentes avec la taille réelles des images à afficher.
|
||||
|
||||
* La page gère correctement l'absence du dossier d'images sans planter, en affichant un message d'erreur.
|
||||
|
||||
* L'endpoint `/api/images` retourne une structure JSON valide en cas de succès.
|
||||
|
||||
* L'endpoint `/api/images` retourne les codes d'erreur HTTP et les messages appropriés (404, 500).
|
||||
|
||||
* L'ensemble des tests fournis (et les vôtre) passent avec succès.
|
||||
|
||||
* Le code dans `views.py` est clair, commenté et suit les bonnes pratiques Python.
|
||||
387
poetry.lock
generated
Normal file
@@ -0,0 +1,387 @@
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "blinker"
|
||||
version = "1.9.0"
|
||||
description = "Fast, simple object-to-object and broadcast signaling"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
|
||||
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.2.1"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
|
||||
{file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""}
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.10.0"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "coverage-7.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cbd823f7ea5286c26406ad9e54268544d82f3d1cadb6d4f3b85e9877f0cab1ef"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab3f7a5dbaab937df0b9e9e8ec6eab235ba9a6f29d71fd3b24335affaed886cc"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8c63aaf850523d8cbe3f5f1a5c78f689b223797bef902635f2493ab43498f36c"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4c3133ce3fa84023f7c6921c4dca711be0b658784c5a51a797168229eae26172"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3747d1d0af85b17d3a156cd30e4bbacf893815e846dc6c07050e9769da2b138e"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:241923b350437f6a7cb343d9df72998305ef940c3c40009f06e05029a047677c"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13e82e499309307104d58ac66f9eed237f7aaceab4325416645be34064d9a2be"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf73cdde4f6c9cd4457b00bf1696236796ac3a241f859a55e0f84a4c58326a7f"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-win32.whl", hash = "sha256:2396e13275b37870a3345f58bce8b15a7e0a985771d13a4b16ce9129954e07d6"},
|
||||
{file = "coverage-7.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d45c7c71fb3d2da92ab893602e3f28f2d1560cec765a27e1824a6e0f7e92cfd"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4abc01843581a6f9dd72d4d15761861190973a2305416639435ef509288f7a04"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2093297773111d7d748fe4a99b68747e57994531fb5c57bbe439af17c11c169"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:58240e27815bf105bd975c2fd42e700839f93d5aad034ef976411193ca32dbfd"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d019eac999b40ad48521ea057958b07a9f549c0c6d257a20e5c7c4ba91af8d1c"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35e0a1f5454bc80faf4ceab10d1d48f025f92046c9c0f3bec2e1a9dda55137f8"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a93dd7759c416dd1cc754123b926d065055cb9a33b6699e64a1e5bdfae1ff459"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7b3d737266048368a6ffd68f1ecd662c54de56535c82eb8f98a55ac216a72cbd"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:93227c2707cb0effd9163cd0d8f0d9ab628982f7a3e915d6d64c7107867b9a07"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-win32.whl", hash = "sha256:69270af3014ab3058ad6108c6d0e218166f568b5a7a070dc3d62c0a63aca1c4d"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c16bbb661a7b4dafac0ab69e44d6dbcc6a64c4d93aefd89edc6f8911b6ab4a"},
|
||||
{file = "coverage-7.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:14e7c23fcb74ed808efb4eb48fcd25a759f0e20f685f83266d1df174860e4733"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2adcfdaf3b4d69b0c64ad024fe9dd6996782b52790fb6033d90f36f39e287df"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d7b27c2c0840e8eeff3f1963782bd9d3bc767488d2e67a31de18d724327f9f6"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0ed50429786e935517570b08576a661fd79032e6060985ab492b9d39ba8e66ee"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7171c139ab6571d70460ecf788b1dcaf376bfc75a42e1946b8c031d062bbbad4"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a726aac7e6e406e403cdee4c443a13aed3ea3d67d856414c5beacac2e70c04e"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2886257481a14e953e96861a00c0fe7151117a523f0470a51e392f00640bba03"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:536578b79521e59c385a2e0a14a5dc2a8edd58761a966d79368413e339fc9535"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77fae95558f7804a9ceefabf3c38ad41af1da92b39781b87197c6440dcaaa967"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-win32.whl", hash = "sha256:97803e14736493eb029558e1502fe507bd6a08af277a5c8eeccf05c3e970cb84"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c73ab554e54ffd38d114d6bc4a7115fb0c840cf6d8622211bee3da26e4bd25d"},
|
||||
{file = "coverage-7.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:3ae95d5a9aedab853641026b71b2ddd01983a0a7e9bf870a20ef3c8f5d904699"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d883fee92b9245c0120fa25b5d36de71ccd4cfc29735906a448271e935d8d86d"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c87e59e88268d30e33d3665ede4fbb77b513981a2df0059e7c106ca3de537586"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f669d969f669a11d6ceee0b733e491d9a50573eb92a71ffab13b15f3aa2665d4"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9582bd6c6771300a847d328c1c4204e751dbc339a9e249eecdc48cada41f72e6"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91f97e9637dc7977842776fdb7ad142075d6fa40bc1b91cb73685265e0d31d32"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae4fa92b6601a62367c6c9967ad32ad4e28a89af54b6bb37d740946b0e0534dd"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a5cc8b97473e7b3623dd17a42d2194a2b49de8afecf8d7d03c8987237a9552c"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc1cbb7f623250e047c32bd7aa1bb62ebc62608d5004d74df095e1059141ac88"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-win32.whl", hash = "sha256:1380cc5666d778e77f1587cd88cc317158111f44d54c0dd3975f0936993284e0"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:bf03cf176af098ee578b754a03add4690b82bdfe070adfb5d192d0b1cd15cf82"},
|
||||
{file = "coverage-7.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:8041c78cd145088116db2329b2fb6e89dc338116c962fbe654b7e9f5d72ab957"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37cc2c06052771f48651160c080a86431884db9cd62ba622cab71049b90a95b3"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:91f37270b16178b05fa107d85713d29bf21606e37b652d38646eef5f2dfbd458"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f9b0b0168864d09bcb9a3837548f75121645c4cfd0efce0eb994c221955c5b10"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0be435d3b616e7d3ee3f9ebbc0d784a213986fe5dff9c6f1042ee7cfd30157"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35e9aba1c4434b837b1d567a533feba5ce205e8e91179c97974b28a14c23d3a0"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a0b0c481e74dfad631bdc2c883e57d8b058e5c90ba8ef087600995daf7bbec18"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8aec1b7c8922808a433c13cd44ace6fceac0609f4587773f6c8217a06102674b"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:04ec59ceb3a594af0927f2e0d810e1221212abd9a2e6b5b917769ff48760b460"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-win32.whl", hash = "sha256:b6871e62d29646eb9b3f5f92def59e7575daea1587db21f99e2b19561187abda"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff99cff2be44f78920b76803f782e91ffb46ccc7fa89eccccc0da3ca94285b64"},
|
||||
{file = "coverage-7.10.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3246b63501348fe47299d12c47a27cfc221cfbffa1c2d857bcc8151323a4ae4f"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:1f628d91f941a375b4503cb486148dbeeffb48e17bc080e0f0adfee729361574"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a0e101d5af952d233557e445f42ebace20b06b4ceb615581595ced5386caa78"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ec4c1abbcc53f9f650acb14ea71725d88246a9e14ed42f8dd1b4e1b694e9d842"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9c95f3a7f041b4cc68a8e3fecfa6366170c13ac773841049f1cd19c8650094e0"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a2cd597b69c16d24e310611f2ed6fcfb8f09429316038c03a57e7b4f5345244"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5e18591906a40c2b3609196c9879136aa4a47c5405052ca6b065ab10cb0b71d0"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:485c55744252ed3f300cc1a0f5f365e684a0f2651a7aed301f7a67125906b80e"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4dabea1516e5b0e9577282b149c8015e4dceeb606da66fb8d9d75932d5799bf5"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-win32.whl", hash = "sha256:ac455f0537af22333fdc23b824cff81110dff2d47300bb2490f947b7c9a16017"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:b3c94b532f52f95f36fbfde3e178510a4d04eea640b484b2fe8f1491338dc653"},
|
||||
{file = "coverage-7.10.0-cp314-cp314-win_arm64.whl", hash = "sha256:2f807f2c3a9da99c80dfa73f09ef5fc3bd21e70c73ba1c538f23396a3a772252"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0a889ef25215990f65073c32cadf37483363a6a22914186dedc15a6b1a597d50"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39c638ecf3123805bacbf71aff8091e93af490c676fca10ab4e442375076e483"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f2f2c0df0cbcf7dffa14f88a99c530cdef3f4fcfe935fa4f95d28be2e7ebc570"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:048d19a5d641a2296745ab59f34a27b89a08c48d6d432685f22aac0ec1ea447f"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1209b65d302d7a762004be37ab9396cbd8c99525ed572bdf455477e3a9449e06"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e44aa79a36a7a0aec6ea109905a4a7c28552d90f34e5941b36217ae9556657d5"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96124be864b89395770c9a14652afcddbcdafb99466f53a9281c51d1466fb741"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aad222e841f94b42bd1d6be71737fade66943853f0807cf87887c88f70883a2a"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-win32.whl", hash = "sha256:0eed5354d28caa5c8ad60e07e938f253e4b2810ea7dd56784339b6ce98b6f104"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3da35f9980058acb960b2644527cc3911f1e00f94d309d704b309fa984029109"},
|
||||
{file = "coverage-7.10.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cb9e138dfa8a4b5c52c92a537651e2ca4f2ca48d8cb1bc01a2cbe7a5773c2426"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cf283ec9c6878826291b17442eb5c32d3d252dc77d25e082b460b2d2ea67ba3c"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a83488c9fc6fff487f2ab551f9b64c70672357b8949f0951b0cd778b3ed8165"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b86df3a7494d12338c11e59f210a0498d6109bbc3a4037f44de517ebb30a9c6b"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6de9b460809e5e4787b742e786a36ae2346a53982e2be317cdcb7a33c56412fb"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de5ef8a5954d63fa26a6aaa4600e48f885ce70fe495e8fce2c43aa9241fc9434"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f178fe5e96f1e057527d5d0b20ab76b8616e0410169c33716cc226118eaf2c4f"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4a38c42f0182a012fa9ec25bc6057e51114c1ba125be304f3f776d6d283cb303"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bf09beb5c1785cb36aad042455c0afab561399b74bb8cdaf6e82b7d77322df99"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-win32.whl", hash = "sha256:cb8dfbb5d3016cb8d1940444c0c69b40cdc6c8bde724b07716ee5ea47b5273c6"},
|
||||
{file = "coverage-7.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:58ff22653cd93d563110d1ff2aef958f5f21be9e917762f8124d0e36f80f172a"},
|
||||
{file = "coverage-7.10.0-py3-none-any.whl", hash = "sha256:310a786330bb0463775c21d68e26e79973839b66d29e065c5787122b8dd4489f"},
|
||||
{file = "coverage-7.10.0.tar.gz", hash = "sha256:2768885aef484b5dcde56262cbdfba559b770bfc46994fe9485dc3614c7a5867"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "3.1.1"
|
||||
description = "A simple framework for building complex web applications."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"},
|
||||
{file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
blinker = ">=1.9.0"
|
||||
click = ">=8.1.3"
|
||||
itsdangerous = ">=2.2.0"
|
||||
jinja2 = ">=3.1.2"
|
||||
markupsafe = ">=2.1.1"
|
||||
werkzeug = ">=3.1.0"
|
||||
|
||||
[package.extras]
|
||||
async = ["asgiref (>=3.2)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.1.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
|
||||
{file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.2.0"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
|
||||
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
description = "A very fast and expressive template engine."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
|
||||
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.2"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
|
||||
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
||||
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
|
||||
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["coverage", "pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
|
||||
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.4.1"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"},
|
||||
{file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = ">=1"
|
||||
packaging = ">=20"
|
||||
pluggy = ">=1.5,<2"
|
||||
pygments = ">=2.7.2"
|
||||
|
||||
[package.extras]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "6.2.1"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"},
|
||||
{file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
coverage = {version = ">=7.5", extras = ["toml"]}
|
||||
pluggy = ">=1.2"
|
||||
pytest = ">=6.2.5"
|
||||
|
||||
[package.extras]
|
||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.1.3"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"},
|
||||
{file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.1.1"
|
||||
|
||||
[package.extras]
|
||||
watchdog = ["watchdog (>=2.3)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.13"
|
||||
content-hash = "45098047af5eb52decb5a21db08c81dcedabc693ecf27dbb96a9f6f09b31a50e"
|
||||
52
pyproject.toml
Normal file
@@ -0,0 +1,52 @@
|
||||
[project]
|
||||
name = "tp-flask"
|
||||
version = "0.1.0"
|
||||
description = "TP flask"
|
||||
authors = [
|
||||
{name = "Your Name",email = "you@example.com"}
|
||||
]
|
||||
readme = "README.md"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3.13",
|
||||
]
|
||||
keywords = ["flask", "web"]
|
||||
|
||||
exclude = [
|
||||
{ path = "tests", format = "wheel" }
|
||||
]
|
||||
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[tool.poetry]
|
||||
package-mode = false
|
||||
packages = [{include = "app", from = "src"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.13"
|
||||
flask = "^3.1.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.4.1"
|
||||
pytest-cov = "^6.2.1"
|
||||
coverage = { version="*", extras=["toml"]}
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
|
||||
[tool.pycln]
|
||||
all = true
|
||||
|
||||
[tool.isort]
|
||||
line_length = 120
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = true
|
||||
ensure_newline_before_comments = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = ["src"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
31
src/app/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import logging
|
||||
from flask import Flask, render_template
|
||||
|
||||
# Utilisation du patron de conception "Factory" pour créer l'application Flask.
|
||||
def create_app() -> Flask:
|
||||
"""Crée et configure une instance de l'application Flask."""
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] - %(message)s')
|
||||
|
||||
app = Flask(__name__, instance_relative_config=True)
|
||||
|
||||
# Enregistrement du Blueprint (collection de routes) contenant nos vues
|
||||
from . import views
|
||||
app.register_blueprint(views.app)
|
||||
|
||||
# --- Gestionnaires d'erreurs HTTP ---
|
||||
# Ils sont rattachés à l'application pour être globaux.
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e: Exception) -> tuple[str, int]:
|
||||
logging.warning(f"Accès à une page inexistante.")
|
||||
return render_template('404.html'), 404
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e: Exception) -> tuple[str, int]:
|
||||
logging.error(f"Erreur interne du serveur : {e}")
|
||||
return render_template('500.html'), 500
|
||||
|
||||
logging.info("Application créée avec succès.")
|
||||
return app
|
||||
6
src/app/static/css/bootstrap.min.css
vendored
Normal file
BIN
src/app/static/images/GSFC_20171208.jpg
Normal file
|
After Width: | Height: | Size: 12 MiB |
BIN
src/app/static/images/GSFC_20171208_100x67.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
src/app/static/images/GSFC_20171208_100x67.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/app/static/images/GSFC_20171208_150x101.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
src/app/static/images/GSFC_20171208_150x101.webp
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/app/static/images/GSFC_20171208_1928x1309.png
Normal file
|
After Width: | Height: | Size: 4.3 MiB |
BIN
src/app/static/images/GSFC_20171208_1928x1309.webp
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
src/app/static/images/GSFC_20171208_2500x1698.png
Normal file
|
After Width: | Height: | Size: 7.4 MiB |
BIN
src/app/static/images/GSFC_20171208_2500x1698.webp
Normal file
|
After Width: | Height: | Size: 308 KiB |
BIN
src/app/static/images/GSFC_20171208_260x176.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
src/app/static/images/GSFC_20171208_260x176.webp
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/app/static/images/GSFC_20171208_4000x2716.png
Normal file
|
After Width: | Height: | Size: 19 MiB |
BIN
src/app/static/images/GSFC_20171208_4000x2716.webp
Normal file
|
After Width: | Height: | Size: 793 KiB |
BIN
src/app/static/images/GSFC_20171208_480x326.png
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
src/app/static/images/GSFC_20171208_480x326.webp
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/app/static/images/GSFC_20171208_50x33.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
src/app/static/images/GSFC_20171208_50x33.webp
Normal file
|
After Width: | Height: | Size: 710 B |
BIN
src/app/static/images/GSFC_20171208_576x391.png
Normal file
|
After Width: | Height: | Size: 393 KiB |
BIN
src/app/static/images/GSFC_20171208_576x391.webp
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src/app/static/images/GSFC_20171208_768x521.png
Normal file
|
After Width: | Height: | Size: 688 KiB |
BIN
src/app/static/images/GSFC_20171208_768x521.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
src/app/static/images/GSFC_20171208_992x673.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/app/static/images/GSFC_20171208_992x673.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
src/app/static/images/ISS070E034016.jpg
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
src/app/static/images/ISS070E034016_100x66.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
src/app/static/images/ISS070E034016_100x66.webp
Normal file
|
After Width: | Height: | Size: 656 B |
BIN
src/app/static/images/ISS070E034016_150x100.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/app/static/images/ISS070E034016_150x100.webp
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/app/static/images/ISS070E034016_1928x1285.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
src/app/static/images/ISS070E034016_1928x1285.webp
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
src/app/static/images/ISS070E034016_2500x1666.png
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
src/app/static/images/ISS070E034016_2500x1666.webp
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
src/app/static/images/ISS070E034016_260x173.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
src/app/static/images/ISS070E034016_260x173.webp
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/app/static/images/ISS070E034016_4000x2666.png
Normal file
|
After Width: | Height: | Size: 7.2 MiB |
BIN
src/app/static/images/ISS070E034016_4000x2666.webp
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
src/app/static/images/ISS070E034016_480x320.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
src/app/static/images/ISS070E034016_480x320.webp
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
src/app/static/images/ISS070E034016_50x33.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/app/static/images/ISS070E034016_50x33.webp
Normal file
|
After Width: | Height: | Size: 296 B |
BIN
src/app/static/images/ISS070E034016_576x384.png
Normal file
|
After Width: | Height: | Size: 199 KiB |
BIN
src/app/static/images/ISS070E034016_576x384.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/app/static/images/ISS070E034016_768x512.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
BIN
src/app/static/images/ISS070E034016_768x512.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/app/static/images/ISS070E034016_992x661.png
Normal file
|
After Width: | Height: | Size: 580 KiB |
BIN
src/app/static/images/ISS070E034016_992x661.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
src/app/static/images/ISS070E052303.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/app/static/images/ISS070E052303_100x56.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
src/app/static/images/ISS070E052303_100x56.webp
Normal file
|
After Width: | Height: | Size: 896 B |
BIN
src/app/static/images/ISS070E052303_150x84.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
src/app/static/images/ISS070E052303_150x84.webp
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/app/static/images/ISS070E052303_1928x1082.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
src/app/static/images/ISS070E052303_1928x1082.webp
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
src/app/static/images/ISS070E052303_2500x1404.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
src/app/static/images/ISS070E052303_2500x1404.webp
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
src/app/static/images/ISS070E052303_260x146.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
src/app/static/images/ISS070E052303_260x146.webp
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/app/static/images/ISS070E052303_4000x2246.png
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
src/app/static/images/ISS070E052303_4000x2246.webp
Normal file
|
After Width: | Height: | Size: 370 KiB |
BIN
src/app/static/images/ISS070E052303_480x269.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
src/app/static/images/ISS070E052303_480x269.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/app/static/images/ISS070E052303_50x28.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/app/static/images/ISS070E052303_50x28.webp
Normal file
|
After Width: | Height: | Size: 348 B |
BIN
src/app/static/images/ISS070E052303_576x323.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
src/app/static/images/ISS070E052303_576x323.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/app/static/images/ISS070E052303_768x431.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
src/app/static/images/ISS070E052303_768x431.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/app/static/images/ISS070E052303_992x557.png
Normal file
|
After Width: | Height: | Size: 549 KiB |
BIN
src/app/static/images/ISS070E052303_992x557.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
src/app/static/images/PIA04921.jpg
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
BIN
src/app/static/images/PIA04921_100x100.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/app/static/images/PIA04921_100x100.webp
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/app/static/images/PIA04921_150x150.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
src/app/static/images/PIA04921_150x150.webp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/app/static/images/PIA04921_1928x1928.png
Normal file
|
After Width: | Height: | Size: 5.0 MiB |
BIN
src/app/static/images/PIA04921_1928x1928.webp
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
src/app/static/images/PIA04921_2500x2500.png
Normal file
|
After Width: | Height: | Size: 8.3 MiB |
BIN
src/app/static/images/PIA04921_2500x2500.webp
Normal file
|
After Width: | Height: | Size: 519 KiB |
BIN
src/app/static/images/PIA04921_260x260.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
src/app/static/images/PIA04921_260x260.webp
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/app/static/images/PIA04921_4000x4000.png
Normal file
|
After Width: | Height: | Size: 20 MiB |
BIN
src/app/static/images/PIA04921_4000x4000.webp
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
src/app/static/images/PIA04921_480x480.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
src/app/static/images/PIA04921_480x480.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
src/app/static/images/PIA04921_50x50.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/app/static/images/PIA04921_50x50.webp
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
src/app/static/images/PIA04921_576x576.png
Normal file
|
After Width: | Height: | Size: 465 KiB |
BIN
src/app/static/images/PIA04921_576x576.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/app/static/images/PIA04921_768x768.png
Normal file
|
After Width: | Height: | Size: 836 KiB |
BIN
src/app/static/images/PIA04921_768x768.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
src/app/static/images/PIA04921_992x992.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
src/app/static/images/PIA04921_992x992.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
src/app/static/images/PIA18033.jpg
Normal file
|
After Width: | Height: | Size: 6.9 MiB |
BIN
src/app/static/images/PIA18033_100x100.png
Normal file
|
After Width: | Height: | Size: 15 KiB |