Semaine 11
This commit is contained in:
@@ -0,0 +1 @@
|
||||
3.12
|
||||
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
# Module 1 — Exercices : Python & Web (HTTP / REST, sans framework)
|
||||
|
||||
Fil rouge : la **gestion d'un parc d'équipements réseau** (serveurs, switchs, routeurs). Tous les exercices restent en **Python pur** (bibliothèque standard uniquement, aucun framework web).
|
||||
|
||||
---
|
||||
|
||||
## Consignes communes à tous les exercices
|
||||
|
||||
**Usage de l'IA — transparence obligatoire.**
|
||||
L'utilisation d'une IA générative (assistant de code, chatbot, complétion automatique) **n'est pas interdite**, mais elle doit être **assumée et documentée**. Chaque rendu **doit obligatoirement contenir un fichier `USAGE-IA.md`**. Un rendu sans ce fichier est considéré comme **incomplet**.
|
||||
|
||||
Le fichier `USAGE-IA.md` doit répondre à ces points :
|
||||
1. **Outils utilisés** : quel(s) outil(s) d'IA, et pour quelles parties de l'exercice.
|
||||
2. **Aide obtenue** : ce que l'IA a proposé (idées, extraits de code, explications). Donnez au moins un exemple de prompt.
|
||||
3. **Ce que vous avez accepté** : ce que vous avez repris, et surtout *pourquoi vous l'avez compris et validé*.
|
||||
4. **Ce que vous avez refusé ou corrigé** : ce que vous avez écarté ou modifié, et *pourquoi* (erreur, hors-sujet, non compris, mauvaise pratique…).
|
||||
5. **Ce que vous avez appris** et ce que vous feriez autrement.
|
||||
|
||||
> Si vous n'avez utilisé aucune IA, écrivez-le explicitement dans `USAGE-IA.md` et décrivez brièvement votre démarche.
|
||||
|
||||
**Règle d'or : vous devez pouvoir expliquer et justifier chaque ligne de votre rendu**, que vous l'ayez écrite vous-même ou non. Le gabarit de `USAGE-IA.md` est fourni en annexe (fin de fichier).
|
||||
|
||||
---
|
||||
|
||||
## Exercice 1.1 — Rapport d'inventaire en Python
|
||||
|
||||
**Compétences visées :** variables, listes, dictionnaires, boucles, conditions, f-strings.
|
||||
|
||||
**Contexte :** on vous fournit l'inventaire du parc sous forme d'une liste de dictionnaires.
|
||||
|
||||
```python
|
||||
equipements = [
|
||||
{"hostname": "srv-web-01", "ip": "192.168.1.10", "type": "serveur", "actif": True, "latence_ms": 12.4},
|
||||
{"hostname": "sw-access-02", "ip": "192.168.1.2", "type": "switch", "actif": True, "latence_ms": 1.2},
|
||||
{"hostname": "rtr-core-01", "ip": "10.0.0.1", "type": "routeur", "actif": False, "latence_ms": 3.5},
|
||||
{"hostname": "sw-access-03", "ip": "192.168.1.3", "type": "switch", "actif": True, "latence_ms": 1.8},
|
||||
]
|
||||
```
|
||||
|
||||
**Consignes :**
|
||||
1. Calculez le **nombre total** d'équipements et le **nombre d'équipements actifs**.
|
||||
2. Construisez la **liste des hostnames** dont le `type` est `"switch"`.
|
||||
3. Calculez la **latence moyenne des équipements actifs**.
|
||||
4. Affichez un rapport lisible (une ligne par indicateur), latence arrondie à **2 décimales**.
|
||||
|
||||
**Livrables :** `inventaire.py`, `USAGE-IA.md`.
|
||||
|
||||
**Critères d'évaluation :** exactitude des calculs, usage correct des boucles/compréhensions et des conditions, lisibilité de la sortie.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Annexe — Gabarit `USAGE-IA.md`
|
||||
|
||||
```markdown
|
||||
# Usage de l'IA — Exercice X.Y
|
||||
|
||||
## 1. Outils utilisés
|
||||
(Ex : assistant de code / chatbot ; pour quelles parties. Ou : « aucune IA utilisée ».)
|
||||
|
||||
## 2. Aide obtenue
|
||||
(Ce que l'IA a proposé. Exemple de prompt utilisé : « … »)
|
||||
|
||||
## 3. Ce que j'ai accepté
|
||||
(Ce que j'ai repris et pourquoi je l'ai compris et validé.)
|
||||
|
||||
## 4. Ce que j'ai refusé ou corrigé
|
||||
(Ce que j'ai écarté / modifié, et pourquoi.)
|
||||
|
||||
## 5. Ce que j'ai appris
|
||||
(Ce que je retiens, ce que je ferais autrement.)
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
## Exercice 1.2 — Modéliser un équipement (POO) & calculer un sous-réseau
|
||||
|
||||
**Compétences visées :** fonctions, POO (classe, `__init__`, `self`, méthode), fonction `lambda`, tri.
|
||||
|
||||
**Consignes :**
|
||||
1. Écrivez une classe `Equipement` avec :
|
||||
- un constructeur `__init__(self, hostname, ip, latence_ms, actif=True)` ;
|
||||
- une méthode `__str__` renvoyant une description lisible ;
|
||||
- une méthode `est_operationnel(self, seuil_ms=100)` qui renvoie `True` si l'équipement est **actif ET** que sa latence est **sous le seuil**.
|
||||
2. Écrivez une fonction `hotes_disponibles(bits_hote)` qui renvoie le nombre d'adresses **hôtes utilisables** d'un sous-réseau (indice : `2 ** bits - 2`).
|
||||
3. Écrivez une fonction `trier_par_latence(equipements)` qui renvoie la liste triée par latence croissante (utilisez `sorted` + `lambda`).
|
||||
4. Créez 3 équipements, affichez-les **triés par latence** avec leur état opérationnel, puis affichez le nombre d'hôtes d'un `/24` (soit 8 bits d'hôte).
|
||||
|
||||
**Livrables :** `equipement.py`, `USAGE-IA.md`.
|
||||
|
||||
**Critères d'évaluation :** POO correcte (`self`, méthodes), `lambda` pour le tri, exactitude du calcul réseau.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
## Exercice 1.3 — API REST d'inventaire **sans framework**
|
||||
|
||||
**Compétences visées :** comprendre HTTP/REST « de l'intérieur » — méthodes, codes de statut, JSON — en n'utilisant **que la bibliothèque standard**.
|
||||
|
||||
**Contexte :** vous exposez l'inventaire via une petite API REST bâtie avec le module `http.server`. **Flask, Django et toute bibliothèque tierce sont interdits.**
|
||||
|
||||
**Consignes :**
|
||||
1. Utilisez `http.server.BaseHTTPRequestHandler` et `HTTPServer`.
|
||||
2. Stockez les équipements dans une **liste Python en mémoire** (`id`, `hostname`, `ip`).
|
||||
3. `GET /equipements` → renvoie la liste en JSON, code **200**.
|
||||
4. `GET /equipements/<id>` → renvoie l'équipement (**200**), une erreur JSON **404** s'il n'existe pas, **400** si l'`id` n'est pas un entier.
|
||||
5. `POST /equipements` → crée un équipement à partir du JSON du corps ; renvoie **201** et l'objet créé ; renvoie **400** si `hostname` manque ou si le JSON est invalide.
|
||||
6. Toutes les réponses portent l'en-tête `Content-Type: application/json`.
|
||||
7. Testez votre API (serveur lancé sur le port 8000) :
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8000/equipements
|
||||
curl http://127.0.0.1:8000/equipements/1
|
||||
curl -X POST http://127.0.0.1:8000/equipements \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"hostname":"srv-dns-01","ip":"192.168.1.20"}'
|
||||
```
|
||||
|
||||
**Livrables :** `api_inventaire.py`, `TESTS.md` (vos commandes `curl` et les réponses obtenues), `USAGE-IA.md`.
|
||||
|
||||
**Critères d'évaluation :** bonnes méthodes et bons codes HTTP, JSON valide, gestion des erreurs (400/404), **aucun framework**.
|
||||
@@ -0,0 +1,15 @@
|
||||
## Exercice 2.3 — Inventaire avec l'ORM SQLAlchemy *(Avancé — 45 min)*
|
||||
|
||||
**Compétences visées :** ORM SQLAlchemy 2.0 (`Mapped` / `mapped_column`), modèles liés (`relationship`, `ForeignKey`), `Session`.
|
||||
|
||||
**Consignes :**
|
||||
1. Définissez deux modèles `Equipement` et `Interface` avec une relation **1-N** (`relationship` + `ForeignKey` + `back_populates`).
|
||||
2. Créez le moteur SQLite et générez les tables (`create_all`).
|
||||
3. Dans une `Session`, créez **un équipement avec ses deux interfaces en une seule fois**, puis `commit`.
|
||||
4. Récupérez les équipements avec `select()` et affichez, pour chacun, la liste de ses interfaces.
|
||||
|
||||
**Livrables :** `inventaire_orm.py`, `USAGE-IA.md`.
|
||||
|
||||
**Critères d'évaluation :** modèles + relation corrects, gestion de la session/commit, requête `select()`, affichage des objets liés.
|
||||
|
||||
---
|
||||
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
# Exercice 1.3 "API REST" - Gauvain BOICHÉ
|
||||
|
||||
### Modèle(s) utilisés
|
||||
|
||||
- [MiniMax M3](https://www.minimax.io/blog/minimax-m3) (version 2026-06-12) avec étendue "Entire Web" activée.
|
||||
|
||||
### Usage de l'IA
|
||||
|
||||
Je déclare avoir usé de l'IA pour :
|
||||
|
||||
- Redéfinir les demandes (pourquoi l'absence de PUT ou DELETE dans une API REST ?) et les contours
|
||||
=> "Sans toutes les méthodes, l'API sera dite REST-incomplète"
|
||||
- Etablir des co-dépendances non mentionnées (comment lire deux listes sans threading ?)
|
||||
=> `import threading`
|
||||
=> `import urlparse`
|
||||
- Quels garde-fous mettre sans connaissance de base ?
|
||||
=> Valider le format de l'adresse IP
|
||||
=> Rejet des doublons
|
||||
=> Gérer un ID non fourni lors d'un POST
|
||||
- Quelle gestion des états ?
|
||||
=> 405 "Method not allowed"
|
||||
=> 415 "Unsupported media type"
|
||||
=> 500 "Internal Server Error"
|
||||
- Sécurité ?
|
||||
=> Appels anonymes, pas de limites ?
|
||||
=> `_next_id = 1`
|
||||
=> `MAX_BODY = 1_048_576 # 1 Mo`
|
||||
|
||||
Alors, ce n'est pas pour autant que j'ai TOUT implémenté. J'ai corrigé un ou deux trucs, mais faire des méthodes PUT et DELETE aurait prit trop de temps. Rédiger ex nihilo des fonctions sur une stack que je ne connais pas, c'est déjà bien assez comme ça.
|
||||
|
||||
### Non-usage de l'IA
|
||||
|
||||
Je déclare n'avoir pas usé de l'IA au profit des documentations officielles pour établir les fonctions.
|
||||
|
||||
Les documentations utilisées :
|
||||
|
||||
- [Exemple de fonctions déjà rédigées](https://gist.github.com/scimad/ae0196afc0bade2ae39d604225084507)
|
||||
- [Doc http.server pour Python 3.12](https://docs.python.org/3.12/library/http.server.html)
|
||||
- [Doc urllib.parse pour Python 3.12](https://docs.python.org/3.12/library/urllib.parse.html)
|
||||
- [Threading](https://docs.python.org/3/library/threading.html#threading.Lock)
|
||||
- [Architecture REST](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)
|
||||
@@ -0,0 +1,130 @@
|
||||
# Exercice 1.3 "API REST" - Gauvain BOICHÉ
|
||||
|
||||
## Commandes lancées
|
||||
|
||||
### 1. Liste vide
|
||||
curl -i http://127.0.0.1:8000/equipments
|
||||
curl -i http://127.0.0.1:8000/equipments/1
|
||||
|
||||
### 2. Création
|
||||
curl -i -X POST http://127.0.0.1:8000/equipments \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"hostname":"srv-dns-01","ip":"192.168.1.20"}'
|
||||
|
||||
### 3. Lecture
|
||||
curl -i http://127.0.0.1:8000/equipments/1
|
||||
|
||||
### 4. Erreurs
|
||||
curl -i http://127.0.0.1:8000/equipments/abc
|
||||
curl -i http://127.0.0.1:8000/equipments/99
|
||||
curl -i -X POST http://127.0.0.1:8000/equipments \
|
||||
-H "Content-Type: application/json" -d '{"ip":"1.1.1.1"}'
|
||||
|
||||
## Résultats
|
||||
|
||||
curl -i -X POST http://127.0.0.1:8000/equipments -H "Content-Type: application/json" -d '{"hostname":"srv-dns-01","ip":"192.168.1.20"}'
|
||||
|
||||
### 1. Liste vide
|
||||
|
||||
#### Côté client
|
||||
```
|
||||
> curl -i http://127.0.0.1:8000/equipments
|
||||
HTTP/1.0 200 OK
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:21:49 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 2
|
||||
|
||||
[]
|
||||
|
||||
> curl -i http://127.0.0.1:8000/equipments/1
|
||||
HTTP/1.0 404 Not Found
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:26:13 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 41
|
||||
|
||||
{"erreur": "\u00e9quipement introuvable"}
|
||||
|
||||
```
|
||||
#### Côté serveur
|
||||
```
|
||||
127.0.0.1 - - [06/Jul/2026 16:21:49] "GET /equipments HTTP/1.1" 200 -
|
||||
127.0.0.1 - - [06/Jul/2026 16:24:39] "GET /equipments/1 HTTP/1.1" 404 -
|
||||
```
|
||||
|
||||
### 2. Création
|
||||
|
||||
#### Côté client
|
||||
```
|
||||
> curl -i -X POST http://127.0.0.1:8000/equipments -H "Content-Type: application/json" -d '{"hostname":"srv-dns-01","ip":"192.168.1.20"}'
|
||||
HTTP/1.0 201 Created
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:26:31 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 57
|
||||
|
||||
{"id": 1, "hostname": "srv-dns-01", "ip": "192.168.1.20"}
|
||||
|
||||
```
|
||||
#### Côté serveur
|
||||
```
|
||||
127.0.0.1 - - [06/Jul/2026 16:26:31] "POST /equipments HTTP/1.1" 201 -
|
||||
```
|
||||
|
||||
### 3. Lecture
|
||||
|
||||
#### Côté client
|
||||
```
|
||||
> curl -i http://127.0.0.1:8000/equipments/1
|
||||
HTTP/1.0 200 OK
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:26:45 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 57
|
||||
|
||||
{"id": 1, "hostname": "srv-dns-01", "ip": "192.168.1.20"}
|
||||
|
||||
```
|
||||
#### Côté serveur
|
||||
```
|
||||
127.0.0.1 - - [06/Jul/2026 16:26:45] "GET /equipments/1 HTTP/1.1" 200 -
|
||||
```
|
||||
|
||||
### 4. Erreurs
|
||||
|
||||
#### Côté client
|
||||
```
|
||||
> curl -i http://127.0.0.1:8000/equipments/abc
|
||||
HTTP/1.0 400 Bad Request
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:27:32 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 27
|
||||
|
||||
{"erreur": "id non entier"}
|
||||
|
||||
> curl -i http://127.0.0.1:8000/equipments/99
|
||||
HTTP/1.0 404 Not Found
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:30:26 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 41
|
||||
|
||||
{"erreur": "\u00e9quipement introuvable"}
|
||||
|
||||
> curl -i -X POST http://127.0.0.1:8000/equipments -H "Content-Type: application/json" -d '{"ip":"1.1.1.1"}'
|
||||
HTTP/1.0 400 Bad Request
|
||||
Server: BaseHTTP/0.6 Python/3.12.12
|
||||
Date: Mon, 06 Jul 2026 14:30:48 GMT
|
||||
Content-Type: application/json
|
||||
Content-Length: 35
|
||||
|
||||
{"erreur": "hostname et ip requis"}
|
||||
```
|
||||
#### Côté serveur
|
||||
```
|
||||
127.0.0.1 - - [06/Jul/2026 16:27:32] "GET /equipments/abc HTTP/1.1" 400 -
|
||||
127.0.0.1 - - [06/Jul/2026 16:30:26] "GET /equipments/99 HTTP/1.1" 404 -
|
||||
127.0.0.1 - - [06/Jul/2026 16:30:48] "POST /equipments HTTP/1.1" 400 -
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
import json
|
||||
import threading
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_lock = threading.Lock()
|
||||
_equipments = []
|
||||
_next_id = 1
|
||||
MAX_BODY = 1_048_576 # 1 Mo
|
||||
|
||||
|
||||
class InventoryHandler(BaseHTTPRequestHandler):
|
||||
def _send_json(self, code, payload):
|
||||
body = json.dumps(payload).encode("utf-8")
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def _read_json(self):
|
||||
length = int(self.headers.get("Content-Length", 0))
|
||||
|
||||
if not length or length > MAX_BODY:
|
||||
raise ValueError("Corps absent ou trop volumineux.")
|
||||
|
||||
if "application/json" not in self.headers.get("Content-Type", ""):
|
||||
raise ValueError("Content-Type invalide.")
|
||||
|
||||
return json.loads(self.rfile.read(length))
|
||||
|
||||
def do_GET(self):
|
||||
path = urlparse(self.path).path
|
||||
parts = path.strip("/").split("/")
|
||||
|
||||
if parts[0] != "equipments":
|
||||
return self._send_json(404, {"erreur": "route inconnue"})
|
||||
|
||||
with _lock:
|
||||
if len(parts) == 1:
|
||||
return self._send_json(200, _equipments)
|
||||
if len(parts) == 2:
|
||||
try:
|
||||
eid = int(parts[1])
|
||||
except ValueError:
|
||||
return self._send_json(400, {"erreur": "id non entier"})
|
||||
for e in _equipments:
|
||||
if e["id"] == eid:
|
||||
return self._send_json(200, e)
|
||||
return self._send_json(404, {"erreur": "équipement introuvable"})
|
||||
|
||||
return self._send_json(405, {"erreur": "méthode non autorisée"})
|
||||
|
||||
def do_POST(self):
|
||||
global _next_id
|
||||
|
||||
if urlparse(self.path).path.rstrip("/") != "/equipments":
|
||||
return self._send_json(404, {"erreur": "route inconnue"})
|
||||
|
||||
try:
|
||||
data = self._read_json()
|
||||
except (ValueError, json.JSONDecodeError) as exc:
|
||||
return self._send_json(400, {"erreur": f"JSON invalide : {exc}"})
|
||||
if not isinstance(data, dict) or "hostname" not in data or "ip" not in data:
|
||||
return self._send_json(400, {"erreur": "hostname et ip requis"})
|
||||
|
||||
with _lock:
|
||||
equipement = {"id": _next_id, "hostname": data["hostname"], "ip": data["ip"]}
|
||||
_equipments.append(equipement)
|
||||
_next_id += 1
|
||||
|
||||
self._send_json(201, equipement)
|
||||
|
||||
if __name__ == "__main__":
|
||||
HTTPServer(("127.0.0.1", 8000), InventoryHandler).serve_forever()
|
||||
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
**Consignes :**
|
||||
1. Écrivez une classe `Equipement` avec :
|
||||
- un constructeur `__init__(self, hostname, ip, latence_ms, actif=True)` ;
|
||||
- une méthode `__str__` renvoyant une description lisible ;
|
||||
- une méthode `est_operationnel(self, seuil_ms=100)` qui renvoie `True` si l'équipement est **actif ET** que sa latence est **sous le seuil**.
|
||||
|
||||
2. Écrivez une fonction `hotes_disponibles(bits_hote)` qui renvoie le nombre d'adresses **hôtes utilisables** d'un sous-réseau (indice : `2 ** bits - 2`).
|
||||
|
||||
3. Écrivez une fonction `trier_par_latence(equipements)` qui renvoie la liste triée par latence croissante (utilisez `sorted` + `lambda`).
|
||||
|
||||
4. Créez 3 équipements, affichez-les **triés par latence** avec leur état opérationnel, puis affichez le nombre d'hôtes d'un `/24` (soit 8 bits d'hôte).
|
||||
"""
|
||||
|
||||
class Equipment():
|
||||
def __init__(self, hostname:str, up:str, latency:float, active:bool=True):
|
||||
self.hostname = hostname
|
||||
self.up = up
|
||||
self.latency = latency
|
||||
self.active = active
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.active:
|
||||
return f"=====\n \
|
||||
HOST : {self.hostname}\n \
|
||||
IS_UP : {self.up}\n \
|
||||
LATENCY : {self.latency}"
|
||||
else:
|
||||
return f"=====\n \
|
||||
Host is currently inactive."
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def is_operational(self, max_latency=100) -> bool:
|
||||
return self.active and self.latency < max_latency
|
||||
|
||||
def available_host(host_bits):
|
||||
return f"{2 ** (32 - host_bits) - 2} hôtes sont disponibles sur {host_bits} bits."
|
||||
|
||||
def sort_by_latency(equipments):
|
||||
return sorted(equipments, key=lambda e: e.latency)
|
||||
|
||||
# Equipements
|
||||
|
||||
e1 = Equipment("Berillium", "Yes", 15, True)
|
||||
e2 = Equipment("Thorium", "No", 7, False)
|
||||
e3 = Equipment("Cadmium", "Yes", 12.4, True)
|
||||
e4 = Equipment("Polonium", "Yes", 145, True)
|
||||
e5 = Equipment("Actinium", "Yes", 2.7, True)
|
||||
|
||||
eX = [e1, e2, e3, e4, e5]
|
||||
|
||||
for e in sort_by_latency(eX):
|
||||
if e.up == "Yes":
|
||||
print(e)
|
||||
|
||||
print(available_host(24))
|
||||
Binary file not shown.
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
**Consignes :**
|
||||
1. Calculez le **nombre total** d'équipements et le **nombre d'équipements actifs**.
|
||||
2. Construisez la **liste des hostnames** dont le `type` est `"switch"`.
|
||||
3. Calculez la **latence moyenne des équipements actifs**.
|
||||
4. Affichez un rapport lisible (une ligne par indicateur), latence arrondie à **2 décimales**.
|
||||
"""
|
||||
|
||||
equipements = [
|
||||
{"hostname": "srv-web-01", "ip": "192.168.1.10", "type": "serveur", "actif": True, "latence_ms": 12.4},
|
||||
{"hostname": "sw-access-02", "ip": "192.168.1.2", "type": "switch", "actif": True, "latence_ms": 1.2},
|
||||
{"hostname": "rtr-core-01", "ip": "10.0.0.1", "type": "routeur", "actif": False, "latence_ms": 3.5},
|
||||
{"hostname": "sw-access-03", "ip": "192.168.1.3", "type": "switch", "actif": True, "latence_ms": 1.8},
|
||||
]
|
||||
|
||||
# 1. Nombre total d'équipements
|
||||
|
||||
def total_equipments(equipments):
|
||||
return f"{len(equipements)} équipements au total, {sum(e["actif"]for e in equipements)} actifs."
|
||||
|
||||
# 2. Liste des noms d'hôtes de type "Switch"
|
||||
|
||||
def name_equipments(equipments):
|
||||
return (e["hostname"] for e in equipments if e["type"]=="switch")
|
||||
|
||||
# 3. Calcul de la latence moyenne des équipements actifs
|
||||
|
||||
def latency_equipments(equipments):
|
||||
return f"{sum(e['latence_ms'] for e in equipments if e['actif']) / sum(e['actif'] for e in equipements):.2f}"
|
||||
|
||||
# 4. Rapport lisible avec une ligne par indicateur
|
||||
|
||||
print(total_equipments(equipements))
|
||||
print(list(name_equipments(equipements)))
|
||||
print(latency_equipments(equipements))
|
||||
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
**Consignes :**
|
||||
|
||||
1. Définissez deux modèles `Equipement` et `Interface` avec une relation **1-N** (`relationship` + `ForeignKey` + `back_populates`).
|
||||
|
||||
2. Créez le moteur SQLite et générez les tables (`create_all`).
|
||||
|
||||
3. Dans une `Session`, créez **un équipement avec ses deux interfaces en une seule fois**, puis `commit`.
|
||||
|
||||
4. Récupérez les équipements avec `select()` et affichez, pour chacun, la liste de ses interfaces.
|
||||
"""
|
||||
|
||||
from sqlalchemy import create_engine, select
|
||||
from sqlalchemy.orm import DeclarativeMeta, declarative_base, sessionmaker
|
||||
|
||||
Base: DeclarativeMeta = declarative_base()
|
||||
db_url = "sqlite:///exo_2_1.db"
|
||||
|
||||
def get_engine():
|
||||
return create_engine(url= db_url)
|
||||
|
||||
def get_session():
|
||||
engine = get_engine()
|
||||
Session = sessionmaker(bind= engine)
|
||||
return Session()
|
||||
|
||||
engine = get_engine()
|
||||
|
||||
from random import choice, randint
|
||||
|
||||
from sqlalchemy import ForeignKey, Column, Integer, Float, String, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
class Equipment(Base):
|
||||
__tablename__ = "equipments"
|
||||
|
||||
id = Column(Integer, primary_key= True, autoincrement= True)
|
||||
hostname = Column(String, default= "[HOSTNAME]", nullable= False)
|
||||
type = Column(String, default= "Server", nullable= False)
|
||||
ip_address = Column(String, default= "255.255.255.0", nullable= False)
|
||||
is_active = Column(Boolean, default= True, nullable= False)
|
||||
latency = Column(Float, nullable= False)
|
||||
|
||||
interfaces = relationship("Interface", back_populates="equipment")
|
||||
|
||||
class Interface(Base):
|
||||
__tablename__ = "interfaces"
|
||||
|
||||
id = Column(Integer, primary_key= True, autoincrement= True)
|
||||
port = Column(Integer, nullable= False)
|
||||
service = Column(String, default= "[SERVICE]", nullable= False)
|
||||
|
||||
equipment_id = Column(Integer, ForeignKey("equipments.id"), nullable= False)
|
||||
|
||||
equipment = relationship("Equipment", back_populates="interfaces")
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
session = get_session()
|
||||
|
||||
def random_ipv4() -> str:
|
||||
return ".".join(str(randint(0, 255)) for _ in range(4))
|
||||
|
||||
def load_words(path: str = "wordlist.txt") -> list[str]:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return [line.strip() for line in f if line.strip()]
|
||||
|
||||
def random_hostname(words: list[str] | None = None) -> str:
|
||||
if words is None:
|
||||
words = load_words()
|
||||
triplet = "-".join(choice(words) for _ in range(3))
|
||||
number = f"{randint(1, 999):03d}"
|
||||
return f"{triplet}-{number}"
|
||||
|
||||
equipment = Equipment(
|
||||
hostname=random_hostname(),
|
||||
type="Server",
|
||||
ip_address=random_ipv4(),
|
||||
is_active=True,
|
||||
latency=12.5,
|
||||
interfaces=[
|
||||
Interface(port=22, service="SSH"),
|
||||
Interface(port=443, service="HTTPS"),
|
||||
],
|
||||
)
|
||||
|
||||
session.add(equipment)
|
||||
session.commit()
|
||||
|
||||
stmt = select(Equipment)
|
||||
for eq in session.execute(stmt).scalars():
|
||||
print(f"{eq.hostname} ({eq.ip_address})")
|
||||
for iface in eq.interfaces:
|
||||
print(f" - port {iface.port} : {iface.service}")
|
||||
@@ -0,0 +1,6 @@
|
||||
def main():
|
||||
print("Hello from jour-01!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "jour-01"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"sqlalchemy>=2.0.51",
|
||||
]
|
||||
Generated
+119
@@ -0,0 +1,119 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.5.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e2/f1/fbbfef6af0bad0548f09bc28948ea3c275b4edb19e17fc5ca9900a6a634d/greenlet-3.5.3.tar.gz", hash = "sha256:a61efc018fd3eb317eeca31aba90ee9e7f26f22884a79b6c6ec715bf71bb62f1", size = 200270, upload-time = "2026-06-26T19:28:24.832Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/6e/4c37d51a2b7f82d2ff11bb6b5f7d766d9a011726624af255e843727627a3/greenlet-3.5.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:719757059f5a53fd0dde23f78cffeafcdd97b21c850ddb7ca684a3c1a1f122e2", size = 288685, upload-time = "2026-06-26T18:22:08.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/73/815dd90131c1b71ebdf53dbc7c276cafec2a1173b97559f97aba72724a87/greenlet-3.5.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efa9f765dd09f9d0cdac651ffdf631ee59ec5dc6ee7a73e0c012ba9c52fbdf5b", size = 604761, upload-time = "2026-06-26T19:07:10.114Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/57/079cfe76bcef36b153b25607ee91c6fcb58f17f8b23c86bbbeabe0c88d72/greenlet-3.5.3-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7faba15ac005376e02a0384504e0243be3370ce010296a44a820feb342b505ab", size = 617044, upload-time = "2026-06-26T19:10:07.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/87/b4d095775a3fb1bcafbb483fc206b27ebb785724c83051447737085dc54e/greenlet-3.5.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:87142215824be6ac05e2e8e2786eec307ccbc27c36723c3881959df654af6861", size = 614244, upload-time = "2026-06-26T18:32:17.594Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/70/7559b609683650fa2b95b8ab84b4ab0b26556a635d19675e12aa832d826d/greenlet-3.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215275b1b49320987352e6c1b054acca0064f965a2c66992bed9a6f7d913f149", size = 1574210, upload-time = "2026-06-26T19:09:03.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/73/be55392074c60fc37655ca40fa6022457bfbf6718e9e342a7b0b41f96dd2/greenlet-3.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b1b0eed82364b0e32c4ea0f221452d33e6bb17ae094d9f72aed9851812747ea", size = 1638627, upload-time = "2026-06-26T18:31:44.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/40/c57489acf8e37d74e2913d4eff63aa0dba17acccc4bdeef874dde2dbbec9/greenlet-3.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:cde8adafa2365676f74a979744629589999093bc86e2484214f58e61df08902c", size = 239882, upload-time = "2026-06-26T18:23:27.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/fd/6fea0e3d6600f785069481ee637e09378dd4118acdfd38ad88ae2db31c98/greenlet-3.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:c4e7b79d83805475f0102008843f6eb45fd3bb0b2e88c774adab5fbaab27117d", size = 238211, upload-time = "2026-06-26T18:22:37.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/ff/a620267401db30a50cc8450ee90730e2d4a85658c055c0e760d4ed47fb13/greenlet-3.5.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c8d87c2134d871df96ecdea9cec7cbaab286dadab0f56476e57aaf9e8ac11550", size = 287609, upload-time = "2026-06-26T18:21:14.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/fa/5401ac78021c826a25b6dde0c705e0a8f29b617509f9185a31dac15fbe1b/greenlet-3.5.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d185dd1621757e70c3861cceffd5317ab4e7ed7eb09c82994828468527ade5", size = 607435, upload-time = "2026-06-26T19:07:11.412Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/76/1dc144a2e56e65d36405078ed774224375ea520a1870a6e46e08bb4ac7bf/greenlet-3.5.3-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c514a468149bf8fbbab874188a3535cd8a48a3e353eb53a3d424296f8dbacd3", size = 619787, upload-time = "2026-06-26T19:10:08.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/87/c298cee62df1de4ad7fec32abda73526cff347fd143a6ed4ac369246668a/greenlet-3.5.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:915f887cf2682b66419b879423a2e072634aa7b7dce6f3ada4957cfced3f1e9a", size = 616786, upload-time = "2026-06-26T18:32:19.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/2e/e6f009885ed0705ccf33fe0583c117cfd03cde77e31a596dd5785a30762b/greenlet-3.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:766cfd421c13e450feb340cd472a3ed9957d438727b7b4593ad7c76c5d2b0deb", size = 1574316, upload-time = "2026-06-26T19:09:04.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/fe/43fd110b01e40da0adb7c90ac7ea744bef2d43dca00de5095fd2351c2a68/greenlet-3.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2ecda9ec22edf38fa389369eaed8c3d37c05f3c54e69f69438dbb2cc1de1458b", size = 1638614, upload-time = "2026-06-26T18:31:46.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/7c/062447147a61f8b4337b156fe70d32a165fcf2f89d7ca6255e572806705c/greenlet-3.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:c82304750f057167ff60d188df1d0cc1764ce9567eadf03e6a7443bcedd0b30b", size = 239850, upload-time = "2026-06-26T18:21:54.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/7e/220a7f5824a64a60443fc03b39dfac4ea63a7fb6d481efa27eafa928e7f4/greenlet-3.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:dc133a1569ee667b2a6ef56ce551084aeefd87a5acbc4736d336d1e2edc6cfc4", size = 238141, upload-time = "2026-06-26T18:22:48.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/93/43e116ee114b28737ba7e12952a0d4e2f55944d0f84e42bc91ba7192a3c9/greenlet-3.5.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:fd2e02fa07485778536a036222d616ab957b1d533f36b3ed98ce725d9c9d3117", size = 288202, upload-time = "2026-06-26T18:23:49.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/2f/146d218299046a43d1f029fd544b3d110d0f175a09c715c7e8da4a4a345d/greenlet-3.5.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df0a0628d1597eb0897b62f55d1343f772405fd25f3b2a796c76874b0c2e22e8", size = 654096, upload-time = "2026-06-26T19:07:12.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/cc/04738cafb3f45fa991ea44f9de94c47dcec964f5a972300988a6751f49d9/greenlet-3.5.3-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ebd933a6adabc298bab47731a130fe6bfb888bd934eee37810f151159544540d", size = 666304, upload-time = "2026-06-26T19:10:09.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/aa/4e0dad5e605c270c784ab911c43da6adb136ccd4d81180f763ca429a723d/greenlet-3.5.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b9d501b40e80b70e32323c799dd9b420a5577a9601469d362ae1ffb690f3a7c", size = 663635, upload-time = "2026-06-26T18:32:20.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/50/13efdbea246fe3d3b735e191fec08fb50809f53cd2383ebe123d0809e44b/greenlet-3.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a1fad1d11e7d6aab184107baa8e4ece11ccba3ec9599cd7efa5ff4d70d43256a", size = 1621252, upload-time = "2026-06-26T19:09:05.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/22/c0a336ae4a1410fd5f5121098e5bfbf1865f64c5ef80b4b5412886c4a332/greenlet-3.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fad5aec764399f1b5cc347ad250a59660f20c8f8888ea6bae1f93b769cce1154", size = 1684824, upload-time = "2026-06-26T18:31:47.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/94/91aec0030bea75c4b3244251d0de60a1f3432d1ecb53ab6c437fb5c3ba61/greenlet-3.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:7669aa24cf2a1041d6f7899575b494a3ab4cf68bfcc8609b1dc0be7272db835e", size = 240754, upload-time = "2026-06-26T18:22:15.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/06/68d0983e79e02138f64b4d303c500c27ddb48e5e77f3debb80888a921eae/greenlet-3.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:5b4807c4082c9d1b6d9eed56fcd041863e37f2228106eef24c30ca096e238605", size = 239549, upload-time = "2026-06-26T18:22:42.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/95/3e161213d7f1d378d15aa9e792093e9bfe01844680d04b7fd6e0107c9098/greenlet-3.5.3-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:271a8ea7c1024e8a0d7dd2be66dd66dda8a07193f41a17b9e924f7600f5b62be", size = 296389, upload-time = "2026-06-26T18:22:20.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/92/715c44721abe2b4d1ae9abde4179411868a5bff312479f54e105d372f131/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19131729ae0ddc3c2e1ef85e650169b5e37ee32e400f215f78b94d7b0d567310", size = 653382, upload-time = "2026-06-26T19:07:14.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/83/37a10372a1090a6624cca8e74c12df1a36c2dc36429ed0255b7fb1aeee23/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1540dd8e5fc2a5aec40fbb98ef8e149fa47c89a4b4a1cf2575a14d3d1869d7a8", size = 659401, upload-time = "2026-06-26T19:10:10.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/e2/d1509cad4207da559cc42986ecdd8fc67ad0d1bba2bf03023c467fd5e0f3/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e81fa194a1d20967877bdf9c7794db2bc99063e5be36aee710c08f04c5bb087f", size = 656969, upload-time = "2026-06-26T18:32:22.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/7d/eaf70de20aadca3a5884aec58362861c64ce45e7b277f47ed026926a3b89/greenlet-3.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55cf4d777485d43110e47133cbba6d74a8885a87ec1227ef0267f9ee80c5aa21", size = 1617822, upload-time = "2026-06-26T19:09:06.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f9/414d38fc400ae4350d4185eaad1827676f7cf5287b9136e0ed1cbbe20a7f/greenlet-3.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:12a248ba75f6a9a236375f52296c498c89ff1d8badf32deb9eca7abd5853f7da", size = 1677983, upload-time = "2026-06-26T18:31:49.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/15/7edb977e08f9bff702fe42d6c902702786ff6b9694058b4e6a2a6ac90e57/greenlet-3.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:efc6bd60ea02e085862c74a3ef64b147ffc6f1a5ea7d9f26e7a939943f68c1e3", size = 243626, upload-time = "2026-06-26T18:24:41.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/8a/93928dce91e6b3598b5e779e8d1fd6576a504640c58e78627077f6a7a91a/greenlet-3.5.3-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:ea03f2f04367845d6b58eeed276e1e56e51f0b97d8ad5a88a7d20a91dc9056cc", size = 288860, upload-time = "2026-06-26T18:22:48.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/ca/69db42d447a1378043e2c8f19c09cbbd1263371505053c496b49066d3d16/greenlet-3.5.3-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78dbef602fda6d97d957eb7937f70c9ce9e9527330347f8f6b6f9e554a9e7a47", size = 659747, upload-time = "2026-06-26T19:07:15.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/0b/af7ac2ef8dd41e3da1a40dda6305c23b9a03e13ba975ec916357b50f8575/greenlet-3.5.3-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f73857adb8fee13fa56c172bd11262f888c0c648f9fea113e777bb2c7904a81", size = 670419, upload-time = "2026-06-26T19:10:12.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/1e/1d51640cacbfc455dbe9f9a9f594c49e4e244f63b9971a2f4764e46cc53d/greenlet-3.5.3-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:232fec92e823addaf02d9472cf7381e24a1d046a6ced1103c5caa4c21b9dfc1d", size = 668787, upload-time = "2026-06-26T18:32:24.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/66/4030d5b0b5894500023f003bb054d9bb354dfbd1e186c3a296759172f5f5/greenlet-3.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:2421c3564da9429d5586d46ca31ebb26516b5498a802cf65c041a8e8a8980d34", size = 1626305, upload-time = "2026-06-26T19:09:08.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/50/5221371c7550108dfa3c378debc41d032aa9c78e89abb01d8011cfc93289/greenlet-3.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e0f0d160f0b2e558e6c75f7930967183255dc9735e5f5b8cae58ee09c9576d8b", size = 1688631, upload-time = "2026-06-26T18:31:51.278Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/5d/00d469daae3c65d2bf620b10eee82eb022127d483c6bc8c69fae6f3fbf17/greenlet-3.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:dd99329bbc15ca78dcc583dba05d0b1b0bae01ab6c2174989f5aaee3e41ac930", size = 241027, upload-time = "2026-06-26T18:22:38.203Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/e8/883785b44c5780ed71e83d3e4437e710470be17a2e181e8b601e2da0dc4a/greenlet-3.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:499fef2acede88c1864a57bb586b4bf533c81e1b82df7ab93451cdb47dfec227", size = 240085, upload-time = "2026-06-26T18:23:54.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/da/4f4a8450962fad137c1c8981a3f1b8919d06c829993d4d476f9c525d5173/greenlet-3.5.3-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:176bc16a721fa5fc294d70b87b4dfa5fbdd251b3da5d5372735ecef9bd7d6d0c", size = 297221, upload-time = "2026-06-26T18:23:27.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/66/b3bfae3e220a9b63ea539a0eea681800c69ab1aada757eae8789f183e7ce/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:629b614d2b786e89c50440e246f33eea78f58a962d0bdbbcc809e6d13605903f", size = 657221, upload-time = "2026-06-26T19:07:16.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/81/b6d4d73a709684fc77e7fa034d7c2fe82cffa9fc920fadcaa659c2626213/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b2e857ae16f5f72142edf75f9f176fe7526ba19a2841df1420516f83831c9f2", size = 663226, upload-time = "2026-06-26T19:10:13.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/07/e210b02b589f16e74ff48b730690e4a34ffe984219fce4f3c1a0e7ec8545/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e515757e2e36bcbf1fad09a46e1557e8b1ae1797d4b44d09da7deed88ad28608", size = 660802, upload-time = "2026-06-26T18:32:26.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/2e/5303eb3fa06bca089060f479707182a93e360683bc252acf846c3090d34e/greenlet-3.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b363d46ed1ea431825fdb01471bb024fc08399bad1572a616e853c7684415adb", size = 1622157, upload-time = "2026-06-26T19:09:09.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/70/50de47a488f14df260b50ae34fb5d56016e308b098eab02c878b5223c26a/greenlet-3.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:e44da2f5bbdaabaf7d80b73dbb430c7035771e9f244e3c8b769715c9d8fa0a16", size = 1681159, upload-time = "2026-06-26T18:31:52.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/13/1055e1dda7882073eda533e2b96c62e55bbd2db7fda6d5ece992febc7071/greenlet-3.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:8ff8bed3e3baa20a3ea261ce00526f1898ad4801d4886fd2220580ee0ad8fadf", size = 244007, upload-time = "2026-06-26T18:22:04.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/0d/ca7d15afbdc397e3401134c9e1800d51d12b829661786187a4ad08fe484f/greenlet-3.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:b7068bd09f761f3f5b4d214c2bed063186b2a86148c740b3873e3f56d79bac31", size = 242586, upload-time = "2026-06-26T18:23:37.93Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jour-01"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "sqlalchemy" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "sqlalchemy", specifier = ">=2.0.51" }]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.51"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/70/e868bc5412acd101a8280f25c95f10eeae0771c4eb806b02491142810ee8/sqlalchemy-2.0.51-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d78702b26ba1c18b2d0fb2ea940ba7f17a9581b42e8361ff93920ebbee1235a", size = 2160291, upload-time = "2026-06-15T16:08:48.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/1c/71ee0f8a6b9d7316a1ccd30430b4c62b6c2e36adc96017a4e3a72dce49d6/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581921d849d6e6f994d560389192955e80e2950e18fcdfe2ccea863e01158e6e", size = 3343835, upload-time = "2026-06-15T16:19:42.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/7c/7ab9f9aadc5944fdd06612484ed7918fe376ad871a5f50404dc1536e0194/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d21ce524ab86c23046e992a5b81cb54c21079c6df6e78b8fc77d77cac70a6b9", size = 3358470, upload-time = "2026-06-15T16:26:38.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/7d/ff77169fee6186de145a7f2b87006c39638391130abbab2b1f63ac6ea583/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c5d98a2709840027f5a347c3af0a7c3d5f6c1ff93af2ca1c54494e23cba8f389", size = 3289874, upload-time = "2026-06-15T16:19:45.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/3b/6c505903710d781b55bc3141ee34a062bf9745a6b5bc7333305b9ed63b33/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1181256e0f16479691b5616d36375dc2620ad8332b25978763c3d206ad3f3f1d", size = 3321692, upload-time = "2026-06-15T16:26:39.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/b7/c5ffe50aa2f4d947c9250e1519d939260329a07fe6272edfccd784b3d007/sqlalchemy-2.0.51-cp312-cp312-win32.whl", hash = "sha256:9f380393be5abeb6815f68fd39271b95127173511b6706b0a630a9995d53f8f5", size = 2119674, upload-time = "2026-06-15T16:23:09.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/dc/46a65916af68a06ef6b972c6050ba4c8f97070fe3fb33097d34229d9bef6/sqlalchemy-2.0.51-cp312-cp312-win_amd64.whl", hash = "sha256:2cf39aabdf48e87c1c2c2ed6d20d33ffa0733b3071ce9c5f66357947dd009080", size = 2146670, upload-time = "2026-06-15T16:23:11.048Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/49/a739be2e1d02a96a658eb71ab45d921c874249252358ad24a5bffdd02525/sqlalchemy-2.0.51-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ea306caaae6bd5afd0a46050003c88f6bf33227377a49298c498c3cb88ff491", size = 2158999, upload-time = "2026-06-15T16:08:51.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/6b/2e0e38cf75c8780eca78d9b2e78164f8bcfd70125e5caa588ff5cbb9c9f4/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c45a496d6bc05dec41dcd4c3a2b183723f47473255c159cd80b503c8f246424d", size = 3282539, upload-time = "2026-06-15T16:19:51.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/a1/e77854cb5336fd37dc3c6ae3b71de242c98caac5725120be0b526b31cbd0/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4004ada0aafe8ae1991b2cd1d99c6d9146126e123bd6f883c260d974aa012e54", size = 3287545, upload-time = "2026-06-15T16:26:44.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/ab/9e17272fd4dac8df3b83c4fbe52b998a1c9d89a843c8c35ff29b74ff7364/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f6bcad487aee1c638d707235682fc96f741de00663619881ab235400d03289e", size = 3230929, upload-time = "2026-06-15T16:19:52.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/3c/52f408ea701781caee975606beccc48845f2aee8711ac29843d612c0306c/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:39a76529db6305693d8d4affa58ad5b5e2e18edd62daea628b29b97930b3513d", size = 3252888, upload-time = "2026-06-15T16:26:46.454Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/16/3efd2ee6bc4ca4693a30a1dd17a91b606cae15d517d2a4746611d9b73ce8/sqlalchemy-2.0.51-cp314-cp314-win32.whl", hash = "sha256:08a204d8b5638717c26a24df18fcf40af45a6b22e35b70b1d62f0113c2e278e8", size = 2120551, upload-time = "2026-06-15T16:23:15.629Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/78/55b12e70f45bccc40d9e483925c065027b3b98ea4cbbdf6f8c2546feaf6c/sqlalchemy-2.0.51-cp314-cp314-win_amd64.whl", hash = "sha256:96747bfbadb055466e5b46d572618170046b45ce5a4879167f50d70a5319a499", size = 2146318, upload-time = "2026-06-15T16:23:17.108Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/db/a9574ed40fed418924b1b1a3e54f47ee3963053b3d3d325a0d36b41f2c08/sqlalchemy-2.0.51-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1a213be1fcd5e49d9904c3b9939211ded90bc2a64e93f4c01963474285de", size = 2178920, upload-time = "2026-06-15T15:59:56.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/90/a1bb5c7cbba76b7bc1fbd586d0a5479a7bc9c27b4a8298f22ec9423b2bb3/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c6b36ed71f41942bdcd2ad2522be46bfce09d5705be5640ecf19bbc7660e4b7", size = 3566534, upload-time = "2026-06-15T15:58:35.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/4b/481f1fed30e0e9e8dd24aecbb49f29eb57fe7657ece5cf06ee9b84bb97d8/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c2c62877097e1a0db401fba5cb4debee33265e5b2a55c4ccb489c02c53b4f72", size = 3535844, upload-time = "2026-06-15T16:02:43.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/71/0aa64aeda645510af0a43f7d9ee70932f0d1dc4263aed34c50ee891d9df3/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0378d055e9e8cd6ce4d8dff683bdd3d7d413533c4ee51d67a2b1e0f9eacc0f23", size = 3475355, upload-time = "2026-06-15T15:58:36.592Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/db/6061db32316446135a3abae5f308d144ab988a34234726042da3e58b1c63/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e46fc36029eff666391e0531e5387b62ce6c4f1d8e50b3fb3099eaca1b42522", size = 3486591, upload-time = "2026-06-15T16:02:45.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c9/f14fdf71bb8957e0c7e39db69bbdf12b5c80f4ef775fdfa127bf4e0d6760/sqlalchemy-2.0.51-cp314-cp314t-win32.whl", hash = "sha256:9161cfc9efce70d1715f47d6ff40f79c6778c00d53be4fbc09d70301e4b83ba7", size = 2151313, upload-time = "2026-06-15T16:03:39.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c6/673e618e6f4f297e126d9b56ea2f6478708f6c1af4e3223835c22e2c3697/sqlalchemy-2.0.51-cp314-cp314t-win_amd64.whl", hash = "sha256:159bb6ba32059f57ad7375a8f50d844dd2f19d14954ecf820cd33e20debd46b2", size = 2186280, upload-time = "2026-06-15T16:03:40.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user