Projets pratiques à réaliser seul ou en équipe pour apprendre à maîtriser les bases de données.
Même si la structure des sites à réaliser est semblable, chaque élève a reçu des données différentes avec des structures différentes. L'énoncé ci-dessous utilise le terme générique "entité" pour faire référence au concept principal attribué à chaque élève. Entité peut donc faire référence aussi bien à
... en fonction du contexte.
Ton objectif est de développer un site dynamique en Python avec Flask, Jinja2 et SQLite3 pour afficher et rechercher des entités. Ces entités sont toutes situées dans des communes.
Ces consignes représentent les exigences minimum à réaliser. Tu peux toujours aller plus loin si tu en as envie.
Identifie les différentes entités de ton modèle afin de créer un schéma entité-associations avec les entités, les associations et les cardinalités.
Pour le schéma, essaie d'aller le plus loin possible dans ta modélisation en identifiant bien les informations redondantes dans les données.
Pour le code Python et la réalisation de la DB, tu peux normalement te contenter des 2 tables principales (communes et ...).
Tu vas devoir créer un programme qui permet d'enregistrer les données dans la base de données. Ce programme va devoir convertir le fichier d'origine au format CSV ou JSON et insérer les données dans la base de données. Pour ce faire, tu vas pouvoir utiliser ChatGPT ou un autre programme d'intelligence artificielle. Tu devras bien lui demander de respecter le schéma de données que tu as analysé. Sinon, il aura tendance à ne créer qu'une seule table, ce qui n'est pas correct.
Dans un premier temps, il n'est pas nécessaire de comprendre parfaitement le code généré par l'IA. Mais d'un point de vue logique la première étape consiste à:
Tu verras que l'intelligence artificielle devrait d'abord insérer certaines entités en vérifiant si elles n'existent pas déjà en base de données. Elle effectuera ensuite des requêtes afin de relier les entités entre elles à l'aide des clés primaires et étrangères.
Tu devras pouvoir identifier l'endroit où ton entité de référence est insérée afin de pouvoir générer le slug (voir plus loin).
Le site consiste en:
Un menu de navigation et un pied de page sur toutes les pages
une page d'accueil
une page avec la liste des communes
une page pour afficher les informations d'une entité
une page de recherche d'entités
une API de recherche de communes
Organise tes templates correctement, en utilisant l'héritage.
Attention: pour générer des chemins vers tes entités, tu vas devoir utiliser ce que l'on appelle des slugs. Ces slugs doivent être générés au moment de l'import des données sur base du nom de l'entité.
Un slug est la partie d’une URL qui est spécifique à la page/ressource que l’on publie et dont on a enlevé tous les caractères spéciaux et remplacé les espaces par des "-".
Exemple: École de cinéma à Paris --> ecole-de-cinema-a-paris
Tes urls ressembleront donc à /cinemas/cinema-l-etoile.
Pour faire simple et efficace, utilise le module "slugify" en pièce jointe:
# Exemple d'utilisation
text = "École de cinéma à Paris"
slug = slugify(text)
print(slug) # Résultat : "ecole-de-cinema-a-paris"
Le menu doit permettre de naviguer sur ton site et contenir un champ de recherche qui pointe vers la page de recherche.

Utilise une navbar de Bootstrap.

La page d'accueil doit contenir:

Cette page affiche la liste complète des communes avec le nombre d'entités qu'elle contient. La liste est affichée par ordre décroissant du nombre d'entités et ensuite par ordre alphabétique.
L'utilisateur doir pouvoir cliquer sur le nom d'une commune pour se rendre sur la page correspondante.
Utlise les class bootstrap suivantes: btn, btn-success, badge. Exemple
<button type="button" class="btn btn-primary">
Notifications <span class="badge text-bg-secondary">4</span>
</button>

Cette page affiche toutes les entités dans une table Bootstrap, triées par ordre alphabétique.
Pour des raisons de performance, tu peux limiter les résultats aux 50 premières entités.

Cette page affiche toutes les entités qui correspondent à la recherche de l'utilisateur sous forme de table Bootstrap.
La recherche doit renvoyer toutes les entités dont le nom ou le nom de la commune contiennent le mot recherché, triées par ordre alphabétique.
Pour des raisons de performance, tu peux limiter les résultats aux 50 premières entités.

Cette page doit afficher les informations principales de l'entité:
Chemin: /api/search
Cette API est une route qui renvoie exclusivement des données brutes, sans HTML.
S'il y a une seule lettre, la route doit renvoyer la liste de toutes les communes qui commencent par cette lettre. Exemple: "J" --> Jodoigne, Jemappe... S'il y a plusieurs lettres, la route doit renvoyer toutes les communes qui contiennent la chaine de caractères. Exemple: "cour" --> Raccour, Incourt...
Les communes doivent être triées par ordre alphabétique.
Votre route DOIT SIMPLEMENT retourner la liste des noms des communes (par exemple return communes):
def recherche(critere):
communes = get_communes(critere) # Ex: ["Incourt", "Jodoigne", "Rixensart"]
return communes
Certains fichiers de données contiennent des caractères spéciaux encodés sous la forme \u00EF19. C'est notamment le cas des caractères accentués. A ma connaissance, ChatGPT ne sait pas comment les traiter correctement.
Lors de mes tests, j'ai trouvé que le plus simple est de remplacer tous ces caractères à la main directement dans le fichier de données avec VS Code avant de l'importer. Sélectionne le code à remplacer, ctrl-f2 (curseurs multiples) et tape le caractère de remplacement.
Le plus simple est de créer 2 fonctions qui retournent les résultats d'une requête passée en paramètre:
💡 Idéalement, tu pourrais créer une fonction pour chaque requête et les placer dans un module séparé pour rednre ton code plus lisible.
# Fonction générique pour exécuter une requête SELECT
def select(query, params=()):
conn = sqlite3.connect("data/cinemas.db")
conn.row_factory = sqlite3.Row # Permet de récupérer les résultats sous forme de dictionnaires
cursor = conn.cursor()
cursor.execute(query, params)
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
Remarque que la connexion est créée et fermée directement dans la fonction.
La fonction select renvoie toujours une liste. Quand tu sais que ta requête ne va renvoyer qu'un seul résultat (count()...) tu peux utiliser cette version qui utilise fetchone() au lieu de fetchall()
# Fonction générique pour exécuter une requête SELECT
def select(query, params=()):
conn = sqlite3.connect("data/cinemas.db")
conn.row_factory = sqlite3.Row # Permet de récupérer les résultats sous forme de dictionnaires
cursor = conn.cursor()
cursor.execute(query, params)
rows = cursor.fetchone()
conn.close()
return dict(row) if row else None
Exemple d'appel de ces fonctions:
age = 18
resultats = select("SELECT * FROM student WHERE age=?", (age,))
Il est possible, et même conseillé d'utiliser des requêtes SQL avec paramètres. Pour ce faire, remplace la valeur à rechercher/utiliser par un ? (appelé place holder) et fournis la valeur en paramètre lors de l'exécution de la requête:
# Requête avec un paramètre
student_name = "Alice"
query = "SELECT * FROM students WHERE name = ?"
cursor.execute(query, (student_name,)) # Note la virgule pour le tuple
Le paramètre passé à execute doit être un tuple (type de variable en Python). S'il n'y a qu'un seul paramètre à passer, il faut ajouter la virgule.
# Requête avec plusieurs paramètres
age = 20
grade = "A"
query = "SELECT * FROM students WHERE age > ? AND grade = ?"
cursor.execute(query, (age, grade)) # Paramètres dans un tuple