Semaine 11

This commit is contained in:
gauvainboiche
2026-07-11 21:38:00 +02:00
parent d3d2416dea
commit 0d071d2843
281 changed files with 54412 additions and 0 deletions
@@ -0,0 +1,180 @@
# Exercice 3.3 - Jinja2 multi-pages
## Navigation + templates dynamiques (sans base de données)
---
## Objectifs pédagogiques
* structurer une application Flask avec plusieurs routes ;
* générer plusieurs pages HTML avec Jinja2 ;
* utiliser des templates réutilisables ;
* exploiter des données simulées côté serveur (Python) ;
* manipuler boucles et conditions dans Jinja2 ;
* construire une navigation entre pages ;
* séparer clairement logique serveur et affichage.
---
# Contexte
Vous développez une mini application de type **tableau de bord de gestion de parc informatique**.
Les données sont **simulées en mémoire (liste Python)**.
Lapplication doit permettre :
* dafficher une liste globale des équipements ;
* de voir uniquement les équipements actifs ;
* de consulter une page de détail par équipement ;
* de naviguer entre les pages via des liens HTML.
---
# Structure proposée
```
app.py
/templates
base.html
index.html
actifs.html
detail.html
```
---
# Travail demandé
---
## Étape 1 - Initialiser lapplication Flask
Créer une application Flask fonctionnelle.
Lapplication doit pouvoir afficher une page daccueil.
---
## Étape 2 - Créer les données simulées
Définir côté serveur une liste Python représentant des équipements.
Chaque équipement contient :
* un identifiant ;
* un nom ;
* un statut (actif / inactif) ;
* une catégorie (serveur, poste, réseau, etc.).
Ces données sont **fixes et non modifiables**.
---
## Étape 3 - Créer une structure de templates
Mettre en place :
* un template principal servant de base commune ;
* une structure avec bloc de contenu réutilisable ;
* un menu de navigation commun à toutes les pages.
Le menu doit permettre de naviguer entre les pages principales.
---
## Étape 4 - Page daccueil (liste complète)
Créer une route et une page affichant :
* la liste complète des équipements ;
* toutes leurs informations.
Dans le template :
* utiliser une boucle Jinja2 ;
* afficher chaque équipement dans un bloc structuré ;
* distinguer visuellement les équipements actifs et inactifs avec une condition.
---
## Étape 5 - Page “équipements actifs”
Créer une page dédiée affichant uniquement :
* les équipements actifs.
La logique de filtrage doit être réalisée côté serveur (Python), pas dans le template.
Dans le template :
* afficher uniquement les éléments fournis par la vue ;
* utiliser une boucle simple sans logique complexe.
---
## Étape 6 - Page de détail dun équipement
Créer une route dynamique permettant dafficher :
* un équipement spécifique selon son identifiant.
La page doit afficher :
* toutes les informations de l’équipement ;
* un message derreur si lidentifiant nexiste pas.
Dans le template :
* utiliser des variables Jinja2 ;
* utiliser une condition pour gérer le cas “non trouvé”.
---
## Étape 7 - Navigation entre pages
Dans le template principal :
* ajouter un menu de navigation commun ;
* permettre daccéder à toutes les pages ;
* assurer une cohérence visuelle sur toutes les vues.
---
## Étape 8 - Mise en forme Jinja2 obligatoire
Dans les templates, vous devez utiliser :
* des boucles `{% for %}` ;
* des conditions `{% if %}` ;
* des variables `{{ }}` ;
* au moins un bloc réutilisable (`block`) ;
* une logique de filtrage côté Python (obligatoire).
---
## Étape 9
Ajouter :
* une page “statistiques” affichant :
* nombre total d’équipements ;
* nombre dactifs ;
* nombre dinactifs ;
* un message conditionnel si la liste est vide dans une page.
---
# Livrables attendus
* application Flask fonctionnelle ;
* plusieurs routes Python ;
* 3 à 4 templates Jinja2 ;
* aucune base de données ;
* aucune méthode POST ;
* aucune modification de données.
---
# Compétences évaluées
| Compétence | Niveau attendu |
| ---------------------- | ------------------------------- |
| Flask routing | Plusieurs routes fonctionnelles |
| Jinja2 | Boucles + conditions maîtrisées |
| Architecture templates | base.html + héritage |
| Séparation logique/vue | filtrage côté Python |
| Navigation web | cohérence UX simple |
| Lecture de données | affichage structuré |
+61
View File
@@ -0,0 +1,61 @@
# Exo 3.3 "Templates dynamiques" - BOICHÉ Gauvain
Je déclare avec fait usage de l'IA :
# Raison 1
Pour de "l'autocomplétion" avec le plan gratuit de Github Copilot (modèle inconnu) pour générer la base <!DOCTYPE> html parce que ça me casse un peu les noix de refaire ça. C'est un genre d'aamorçage qui devrait exister de base dans les IDE avec un algo à l'ancienne ultra-léger...
## Pourquoi cet usage
Parce que ça me gonfle de refaire ça pour la Xième fois parce que je ne l'ai pas retenu tellement l'HTML me gonfle.
## Ce que j'en retiens
Je dois trouver une extension qui le fait pour moi.
# Raison 2
Comprendre pourquoi mes pages Jinja2 ne "marchent pas".
## Pourquoi cet usage
Refaisant un document <!DOCTYPE> pour chaque page, je n'ai pas su comprendre par les docs accessibles (qui donnaient juste les blocs avec % et variables Python) qu'il fallait les peupler UNIQUEMENT des éléments étendant la "base". Que l'HTML s'oubliait sur ces parties-là. Je prenais les blocs en le mettant dans des balises <body> etc sans succès.
## Ce que j'en ai retenu
Les patrons Jinja2 héritent de TOUT, et on ne rajotue que ce qu'il y a besoin. y compris les bases DOCTYPE.
# Raison 3
## Pourquoi cet usage
Je cliquais sur "actives" et "details" mais je restait avec le contenu de "index". Je demande une revue de code à MiniMax M3, qui me diagnostique trois possibilités :
| Symptom | Likely cause | Remedy |
|---------|--------------|--------|
|Same content on every page | Browser cache serving the old HTML | Hard-reload: Ctrl + F5 (or Cmd + Shift + R on macOS) |
URL changes but body identical | A single route handling all paths | Verify each @app.route is distinct |
Template not overridden | Missing {% block content %} in child | Ensure children declare the block |
J'essaye, et rien.
## Ce que j'en ai retenu
Que je suis un imbécile. Je demande à l'IA de m'aider, et rien n'y a fait. Jusqu'à ce que je vois ma connerie :
```
@app.route('/')
def index():
return render_template('index.html', username=USERNAME, equipments=EQUIPMENTS)
@app.route('/actives')
def actives():
return render_template('index.html', username=USERNAME, equipments=EQUIPMENTS)
@app.route('/details')
def details():
return render_template('index.html', username=USERNAME, equipments=EQUIPMENTS)
```
Trois fois "index.html". Bon. Résultat, je corrige et ça marche. L'IA ne m'a servi à RIEN ici, sauf ceci : des fois, il faut aller faire un tour, boire de l'eau, revenir et relire.
+54
View File
@@ -0,0 +1,54 @@
from flask import Flask, render_template, abort
app = Flask(__name__)
EQUIPMENTS = [
{"id": 1, "hostname": "srv-web-main-01", "type": "server", "active": True},
{"id": 2, "hostname": "sw-access-01", "type": "switch", "active": True},
{"id": 3, "hostname": "srv-api-01", "type": "server", "active": True},
{"id": 4, "hostname": "waf-front-01", "type": "waf", "active": True},
{"id": 5, "hostname": "individual-alice-01", "type": "instance", "active": True},
{"id": 6, "hostname": "sw-access-02", "type": "switch", "active": False},
{"id": 7, "hostname": "individual-bob-01", "type": "instance", "active": False},
{"id": 8, "hostname": "srv-web-replica-01", "type": "server", "active": True},
]
USERNAME = "administratort"
@app.route('/')
def index():
return render_template('index.html', username=USERNAME, equipments=EQUIPMENTS)
@app.route('/actives')
def actives():
ACTIVE_EQUIPMENTS = [e for e in EQUIPMENTS if e["active"]]
return render_template('actives.html', username=USERNAME, equipments=ACTIVE_EQUIPMENTS)
@app.route('/list')
def details():
return render_template('list.html', username=USERNAME, equipments=EQUIPMENTS)
@app.route("/details/<int:equipment_id>")
def equipment_detail(equipment_id):
equipment = next((e for e in EQUIPMENTS if e["id"] == equipment_id), None)
return render_template(
"details.html",
username=USERNAME,
equipment=equipment,
)
@app.route("/stats")
def stats():
total = len(EQUIPMENTS)
active = sum(1 for e in EQUIPMENTS if e["active"])
inactive = total - active
return render_template(
"stats.html",
username=USERNAME,
total=total,
active=active,
inactive=inactive,
)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
@@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block title %}Active devices - Network Inventory{% endblock %}
{% block content %}
<h1>Active devices</h1>
<p>Signed-in as <strong>{{ username }}</strong>.</p>
<ul>
{% for equipment in equipments %}
<li>{{ equipment.hostname }}</li>
{% else %}
<li><em>No active device.</em></li>
{% endfor %}
</ul>
{% endblock %}
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}NETWORK INVENTORY{% endblock %}</title>
<style>
body { font-family: sans-serif; margin: 2rem; }
header, footer { background: #f4f4f4; padding: 1rem; }
nav a { margin-right: 1rem; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ccc; padding: 0.5rem; text-align: left; }
th { background: #eee; }
.up { color: green; font-weight: bold; }
.down { color: red; font-weight: bold; }
</style>
</head>
<body>
<header>
<nav>
<strong>- MENU -</strong>
<a href="{{ url_for('index') }}">Home</a>
<a href="{{ url_for('actives') }}">Active devices</a>
<a href="{{ url_for('details') }}">Details</a>
<a href="{{ url_for('stats') }}">Stats</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>Gauvain B. - Flask Jinja2 app</footer>
</body>
</html>
@@ -0,0 +1,43 @@
{% extends "base.html" %}
{% block title %}Equipment - Network Inventory{% endblock %}
{% block content %}
<p>Signed-in as <strong>{{ username }}</strong>.</p>
{% if equipment %}
<h1>{{ equipment.hostname }}</h1>
<table>
<tbody>
<tr>
<th>ID</th>
<td>{{ equipment.id }}</td>
</tr>
<tr>
<th>Hostname</th>
<td>{{ equipment.hostname }}</td>
</tr>
<tr>
<th>Type</th>
<td>{{ equipment.type }}</td>
</tr>
<tr>
<th>State</th>
<td>
{% if equipment.active %}
<span class="up">UP</span>
{% else %}
<span class="down">DOWN</span>
{% endif %}
</td>
</tr>
</tbody>
</table>
{% else %}
<h1>There's no device with this ID</h1>
<p>The requested equipment (ID {{ equipment_id }}) does not exist.</p>
{% endif %}
<p><a href="{{ url_for('details') }}">← Back to all devices</a></p>
{% endblock %}
@@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block title %}Home - Network Inventory{% endblock %}
{% block content %}
<h1>Welcome, {{ username }}.</h1>
{% if username == "administratort" %}
<p>You signed-in with ADMIN rights.</p>
{% else %}
<p>You signed-in with USER rights.</p>
{% endif %}
<h2>Devices list:</h2>
<ul>
{% for equipment in equipments %}
<li>{{ equipment.hostname }}</li>
{% endfor %}
</ul>
{% endblock %}
@@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block title %}Details - Network Inventory{% endblock %}
{% block content %}
<h1>Device details</h1>
<p>Signed-in as <strong>{{ username }}</strong>.</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>Hostname</th>
<th>Type</th>
<th>State</th>
</tr>
</thead>
<tbody>
{% for equipment in equipments %}
<tr>
<td>{{ equipment.id }}</td>
<td>
<a href="{{ url_for('equipment_detail', equipment_id=equipment.id) }}">
{{ equipment.hostname }}
</a>
</td>
<td>{{ equipment.type }}</td>
<td>
{% if equipment.active %}
<span class="up">UP</span>
{% else %}
<span class="down">DOWN</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
@@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Statistics - Network Inventory{% endblock %}
{% block content %}
<h1>Inventory statistics</h1>
<p>Signed-in as <strong>{{ username }}</strong>.</p>
{% if total > 0 %}
<table>
<thead>
<tr>
<th>Metric</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>Total equipments</td>
<td>{{ total }}</td>
</tr>
<tr>
<td>Active</td>
<td><span class="up">{{ active }}</span></td>
</tr>
<tr>
<td>Inactive</td>
<td><span class="down">{{ inactive }}</span></td>
</tr>
</tbody>
</table>
{% else %}
<p><em>There's no equipment in the inventory.</em></p>
{% endif %}
{% endblock %}