Ce BE est le premier devoir à rendre concernant INF-TC2. Le compte-rendu (CR) de votre travail devra être déposé sur `Pedagogie1`, sur l'espace de dépôt spécifique à votre groupe. Et cela dans un **délai d'une semaine après la dernière séance consacrée à ce BE** (délai de rigueur, aucun travail accepté au delà de cette date). Cette semaine ne tient pas compte d'éventuelles vacances. Si vous avez un doute, le plus simple est de contrôler la date pour votre groupe sur `Pedagogie1`.
Ce BE est le premier devoir à rendre concernant INF-TC2. Le compte-rendu (CR) de votre travail devra être déposé sur _Pedagogie1_, sur l'espace de dépôt spécifique à votre groupe. Et cela dans un **délai d'une semaine après la dernière séance consacrée à ce BE** (délai de rigueur, aucun travail accepté au delà de cette date). Cette semaine de délai ne tient pas compte d'éventuelles vacances. Si vous avez un doute, le plus simple est de contrôler la date pour votre groupe sur _Pedagogie1_.
**Consignes:**
- Le travail peut être individuel ou en binôme. Si vous travaillez en binôme, **un seul dépôt suffit !**.
- Le dépôt consistera en une unique archive (zip, rar ou tgz) contenant l'ensemble des fichiers suivants :
> - Le travail peut être individuel ou en binôme. Si vous travaillez en binôme, **un seul dépôt suffit !**.
> - Le dépôt consistera en une unique archive (zip, rar ou tgz) contenant l'ensemble des fichiers suivants :
- La base de données *Hotellerie.db*, après exécution des requêtes de l'énoncé.
- Le fichier _HotelDB.py_, contenant la classe **HotelDB** et un programme principal permettant de rejouer l'ensemble des requêtes de cet énoncé.
- La base de données de votre choix pour les 2 requêtes libres : si vous choisissez cette option, n'oubliez pas d'inclure la nouvelle bdd dans votre archive (si elle est très volumineuse, donnez uniquement le chemin de téléchargement dans votre rapport), ainsi que le fichier _Python_ qui réalise les requêtes que vous aurez imaginées.
- La base de données de votre choix pour les 2 requêtes libres (si différentes de *Hotellerie.db*) ; Si elle est très volumineuse, donnez uniquement le chemin de téléchargement dans votre rapport). Ajoutez le fichier _Python_ qui met en œuvre les requêtes que vous aurez imaginées.
- Un rapport (format _pdf_ exclusivement) contenant
- une en-tête où devront figurer le nom des élèves, leur numéro de groupe, le nom de l'encadrant ainsi que le titre du BE.
- des commentaires sur la programmation de chacune des requêtes et les résultats obtenus.
- tout diagramme, toute figure ou toute explication que vous jugerez utile, mais dans un **nombre de pages limité à 10** (il n'est pas demandé de rédiger 10 pages, c'est une limite à ne pas dépasser !).
- L'archive devra nécessairement porter le nom suivant : *nom1-BE3.zip* ou *nom1-nom2-BE3.zip* (pour les étourdis, pensez à remplacer *nom1* et *nom2* par vos propres noms :-) )
> - L'archive devra nécessairement porter le nom suivant : *nom1-BE3.zip* ou *nom1-nom2-BE3.zip* (pour les étourdis, pensez à remplacer *nom1* et *nom2* par vos propres noms :-) )
**Critères d'évaluation du travail**
Voici une liste (non exhaustive) de critères qui permettront à vos encadrants d'évaluer vos requêtes :
-**Qualité du rapport** : apparence visuelle globale, orthographe, structure du rapport claire et cohérente. Qualité des représentations graphique (légendes, label sur les axes...).
> - **Qualité du rapport** : apparence visuelle globale (lisibilité), orthographe, structure du rapport claire et cohérente. Qualité des représentations graphique (légendes, label sur les axes...).
-**Qualité de l'API** : choix des noms et arguments des méthodes, pas de requêtes SQL hors de la classe, affichages textuel et graphique forcément en dehors des méthodes-requêtes.
> - **Qualité de l'API** : choix des noms et arguments des méthodes, pas de requêtes SQL hors de la classe, code d'affichage des texte et des graphiques forcément en dehors des méthodes-requêtes.
-**Qualité du code** : est-ce qu'il fonctionne sans erreur, le code est-il suffisamment commenté et aéré ? Tests multiples avec des paramètres inattendus ou erronés. Interception des erreurs potentielles par des exceptions.
> - **Qualité du code** : est-ce qu'il fonctionne sans erreur, le code est-il suffisamment commenté et aéré ? Tests multiples avec des paramètres inattendus ou erronés. Interception des erreurs potentielles par des exceptions.
-**Qualité des requêtes** : originalité et justification de l'intérêt, difficulté technique, valorisation dans le rapport.
> - **Qualité des requêtes** : originalité et justification de l'intérêt, difficulté technique, valorisation dans le rapport.
L'objectif principal de ce BE concerne l'utilisation **des exceptions** pour améliorer la robustesse d'un code. Pour expérimenter ce concept clé, nous nous servirons du prétexte de la manipulation de bases de données à l'aide de requêtes _SQL_ (*Structured Query Language*), écrites en _Python_. Ce BE est décomposé en trois parties :
__Ceci est la version enseignants incluant les corrections__
__Note aux enseignants__ Ce document est la __version enseignant__ du BE #3 incluant les solutions aux questions posées. L'énoncé porte sur l'utilisation des exceptions, dans le contexte de la manipulation de bases SQL en POO. Il reprend la même librairie (_sqlite3_) que le BE #1 de INF-TC3. Ce BE doit également permettre aux étudiants d'utiliser la librairie graphique orientée objet _matplotlib.pyplot_ pour illustrer les requêtes de la dernière partie (cf _Requêtes libres_).
Ce BE sera noté, les consignes pour rendre le travail sont expliquées dans le fichier _consignes_BE#3.md_, disponible au même endroit que cet énoncé. Pendant les 2 premières heures, les élèves vont apprendre à gérer des exceptions provoquées par des requêtes SQL avec la librairie _sqlite3_ (et se remémorer la syntaxe _SQL_). Les 2 heures suivantes (en autonomie) sont consacrées au design et à l'implémentation de 2 requêtes, à partir d'une bdd de leur choix. Cela doit permettre à chaque étudiant de faire un rendu personnalisé (requêtes et affichages graphiques des résultats), tout en prenant soin de la robustesse de leur code grâce aux exceptions. Cela devrait favoriser un travail personnel des étudiants/binômes puisque chaque requête doit être unique.
Dans le support de cours, les explications concernant la gestion des exceptions commencent à la planche #55 (cf fichier _Python++.pdf_ sur _Pedagogie1_).
---
L'objectif principal de ce BE concerne l'utilisation **des exceptions** ([lien](https://docs.python.org/fr/3/tutorial/errors.html) officiel, [tuto _devstory_](https://devstory.net/11421/python-exception-handling) sur les exceptions) pour améliorer la robustesse d'un code. Pour expérimenter ce concept clé, nous nous servirons du prétexte de la manipulation de bases de données à l'aide de requêtes _SQL_ (_Structured Query Language_), écrites en _Python_. Ce BE est composé de trois parties :
1.**La première partie** (durée: 45 min.) présente quelques commandes élémentaires pour interroger une base _SQL_ à partir de _Python_;
1.**La seconde partie** (durée: 75 min.) permet de découvrir comment manipuler une base de données _SQL_ en _Python_ orienté objet.
1.**La troisième partie** (durée: 120 min. et +) vous engage dans un travail plus personnalisé, pour mettre à profit vos connaissances sur la gestion des exceptions, la librairie graphique *matplotlib* et le langage d'interrogation de bases _SQL_.
Ce BE fera l'objet d'un compte-rendu (CR), seul ou en binôme. Avant de commencer, veuillez prendre connaissance des consignes concernant le rendu du travail (à respecter scrupuleusement) qui se trouvent dans le fichier [consignes_BE#3.md](./consignes_BE#3.md)(dans le même répertoire que cet énoncé). Les critères de notations y sont donnés.
Ce BE fera l'objet d'un compte-rendu (CR), seul ou en binôme. Avant de commencer, veuillez prendre connaissance des consignes concernant le rendu du travail (à respecter scrupuleusement) qui se trouvent dans le fichier [consignes_BE#3.md](./consignes_BE#3.md)(dans le même répertoire que cet énoncé). Les critères de notation y sont présentés.
---
## 1. Mini-tutoriel sur la base Hotellerie.db (45 min.)
Le système de gestion de base de données qui sera utilisé durant ce BE est _SQLite_. Ce système très simple stocke une base de données dans un fichier d'extension _.sqlite_. La base de tests que nous allons utiliser dans cette partie (_hotellerie.db_) est disponible au même endroit que cet énoncé. Son schéma est le suivant :
Le système de gestion de base de données (SGBD) qui sera utilisé durant ce BE est _SQLite_. Ce système stocke une base de données dans un fichier d'extension _.sqlite_. La base de tests que nous allons utiliser dans cette partie (_hotellerie.db_) est disponible au même endroit que cet énoncé. Son schéma est le suivant :
@@ -27,7 +36,7 @@ La base est composée de 5 tables, ces tables étant composées d'un nombre vari
### 1.1 DB browser for SQLite (15 min.)
Toutes les opérations sur une base de données de ce type peuvent être effectuées en _Python_ via les classes et les méthodes présentes au sein du module _sqlite3_. Pour manipuler de manière interactive le contenu de la base (créer, supprimer ou modifier des tables et des enregistrements, effectuer des requêtes _SQL_...), il existe des outils adaptés. L'outil retenu dans le cadre de ce BE s'appelle ``DB Browser for SQLite``. C'est un logiciel libre qui existe pour toutes les plate-formes : _Windows_, _MacOs_, nombreuses distributions _Linux_ et _Unix_...
Toutes les opérations sur une base de données de ce type peuvent être effectuées en _Python_ via les classes et les méthodes présentes au sein du module _sqlite3_ ([doc officielle de la librairie](https://docs.python.org/3/library/sqlite3.html)). Pour manipuler de manière interactive le contenu de la base (créer, supprimer ou modifier des tables et des enregistrements, effectuer des requêtes _SQL_...), il existe des outils adaptés. L'outil retenu dans le cadre de ce BE s'appelle ``DB Browser for SQLite``. C'est un logiciel libre qui existe pour toutes les plate-formes : _Windows_, _MacOs_, nombreuses distributions _Linux_ et _Unix_...
- Téléchargez et installez [DB Browser for SQLite](https://sqlitebrowser.org/) en suivant les instructions d'installation adaptées à votre système d'exploitation.
- Avec ce logiciel, ouvrez la base *hotellerie.db* et naviguez dans les tables (onglet `Structure de la Base de Données`) et les enregistrements (onglet `Parcourir les données`) pour prendre connaissance de la base (telle qu'elle est schématisée ci-dessus).
...
...
@@ -40,15 +49,15 @@ SELECT nom, ville
FROM hotel;
```
La réponse apparaît sous forme de 12 lignes. Ça vous rappelle des choses ? Si non, alors voici quelques pointeurs pour vous rafraîchir la mémoire :
- [cours tutoriel sur SQL](https://www.1keydata.com/fr/sql/)
**Attention** Avant de lancer un requête sur la bdd avec _Python_, il est fortement conseillé de fermer `DB Browser for SQLite`, sinon vous pourriez soit avoir un plantage de votre programme, soit détruire la bdd (auquel cas il vous suffirait de la télécharger à nouveau).
**Attention** Avant de lancer un requête sur la bdd avec _Python_, il est fortement conseillé de fermer `DB Browser for SQLite`, sinon vous pourriez soit avoir un plantage de votre programme, soit détruire la bdd (auquel cas il vous suffirait de la télécharger à nouveau depuis ce site Web).
Nous allons à présent chercher à reproduire la requête ci-dessus en utilisant _Python_ et le package _sqlite3_. C'est une librairie objet dont la [documentation](https://docs.python.org/3/library/sqlite3.html#module-sqlite3) fournit une description des classes et des méthodes disponibles. Suivez le guide...
...
...
@@ -73,9 +82,9 @@ if __name__ == '__main__':
curseur=conn.cursor()# objet permettant de faire des requêtes
curseur.execute("SELECT nom, ville FROM hotel;")# requête SQL
ligne1=curseur.fetchone()# recupère la 1ère ligne du résultat de la requête
ligne1=curseur.fetchone()# récupère la 1ère ligne du résultat de la requête
print('ligne1 =',ligne1)
ligneAll=curseur.fetchall()# recupère toutes les lignes du résultat de la requête
ligneAll=curseur.fetchall()# récupère toutes les lignes suivantes
print('ligneAll =',ligneAll)
conn.close()
...
...
@@ -91,7 +100,7 @@ Copiez et exécutez ce programme ; le résultat se présente sous forme d'un tup
print(ligneAll[0][0])
```
Voici un usage intéressant à étudier :
Voici un usage intéressant à tester :
```python
importsqlite3
if__name__=='__main__':
...
...
@@ -106,10 +115,10 @@ if __name__ == '__main__':
### 1.3 La gestion des exceptions (15 min.)
Les quelques lignes de code que nous venons d'étudier peuvent être à l'origine de nombreux problèmes lors de l'exécution :
Les quelques lignes de code précédentes peuvent être à l'origine de nombreux problèmes lors de l'exécution :
- la base de données est introuvable;
- le nom des tables ou des champs dans la requête sont erronés;
- les noms des tables et/ou des champs dans la requête sont erronés;
- ...
Voyons maintenant comment intercepter les exceptions lancées dans ces cas de figure par les méthodes de la librairie `sqlite3`. Pour cela, veuillez d'abord copier et exécuter le programme suivant :
...
...
@@ -132,13 +141,36 @@ if __name__ == '__main__':
A priori, ce programme ne lance pas d'exception. Modifiez-le pour faire apparaître les 2 problèmes énoncés ci-dessus. Pour cela :
- Changez le nom de la bdd dans le programme, p. ex. _impossible.db_. Comment interprétez-vous le message d'erreur en observant le contenu de votre répertoire de travail ?
- Revenez au nom correct du fichier : _hotellerie.db_. Testez alors le changement de nom des tables, puis des champs.
- Revenez au nom correct du fichier : _hotellerie.db_. Testez alors des noms erronés pour les tables, puis pour les champs.
On constate que, dans chaque situation, l'exception `OperationalError` est lancée, avec des messages d'information différents qui précisent le type d'erreur. Remplacez alors le code `except Exception as err:` par le code `except sqlite3.OperationalError as err:`, et vérifiez que cela fonctionne de la même manière que précédemment.
On constate, dans chaque situation, que l'exception `sqlite3.OperationalError` est lancée, avec des messages d'information différents qui précisent le type d'erreur. Remplacez alors le code `except Exception as err:` par le code `except sqlite3.OperationalError as err:`, et vérifiez que cela fonctionne de la même manière que précédemment.
La liste des exceptions lancées par l'usage de commandes `sqlite3` est disponible en suivant [ce lien](https://docs.python.org/3/library/sqlite3.html#exceptions). Prenez le temps de lire la description des différentes exceptions. Toutes les exceptions de ce module héritent d'une classe appelée __sqlite3.Error__ (elle-même héritant de la classe de base de gestion des exceptions de _Python_ : __Exception__).
---
<p class=correction>
<b>Éléments de réponse pour les enseignants</b>
</p>
Si on donne un mauvais nom pour la Bdd, alors elle est ouverte en écriture (et donc vide), d'où l'erreur sur le nom de la table.
```python
# programme importBdd_3.py
import sqlite3
if __name__ == '__main__':
try:
conn = sqlite3.connect('hotellerie.db')
curseur = conn.cursor()
curseur.execute("SELECT nom, ville FROM hotel;")
print(curseur.fetchall())
except sqlite3.OperationalError as err:# interception d'une exception spécifique
-Commentsecomportevotreprogrammesioninsèrecetappel`aHotelDB.get_name_hotel_etoile("Hello")` ? Truc:exception standard **TypeError**.
- Comment se comporte votre programme si on insère cet appel `aHotelDB.get_name_hotel_etoile(-1)` dans le programme principal ? **Truc:** exception standard **ValueError** ou **AssertionError**.
- Comment se comporte votre programme si on insère cet appel `aHotelDB.get_name_hotel_etoile("Hello")` ? **Truc:** exception standard **TypeError**.
Veillez à ce que ces _appels erronés_ renvoient une liste vide tout simplement.
---
<pclass=correction>
<b>Éléments de réponse pour les enseignants</b>
</p>
_Remarque_ : insister sur la gestion des exceptions pour traiter des arguments erronés
en type (_P. ex._ chaîne de caractères au lieu d'un entier).
```python
importsqlite3
classHotelDB:
def__init__(self,nom):
self.__DBname=nom
self.__conn=sqlite3.connect(self.__DBname)
defget_name_hotel_etoile(self,nbetoiles):
curseur=self.__conn.cursor()
liste=[]
try:
# assert nbetoiles>0, "Le nbre d'étoiles doit être > 0 !" # peut remplacer la suivante
ifnbetoiles<=0:
raiseValueError("Le nbre d'étoiles doit être > 0 !")
#curseur.execute('SELECT nom FROM hotel WHERE etoiles=%d' % nbetoiles) # obsolete
#curseur.execute('SELECT nom FROM hotel WHERE etoiles=?', (nbetoiles,)) # secure
curseur.execute('SELECT nom FROM hotel WHERE etoiles={}'.format(nbetoiles))
exceptsqlite3.OperationalErrorassqlerr:
print('{} in {}'.format(str(sqlerr),self.__DBname))
print("Liste des noms d'hotel",nbEtoiles,"étoiles : ",resultat)
print("Liste des noms d'hotel : ",aHotelDB.get_name_hotel_etoile(-1))
print("Liste des noms d'hotel : ",aHotelDB.get_name_hotel_etoile("Hello"))
```
Ce qui donne
```
Liste des noms d'hotel 2 étoiles : [('La nuit noire',), ('Hotel chez soi',), ('Chez Philippe',)]
ValueError: Le nbre d'étoiles doit être > 0 !
Liste des noms d'hotel : []
TypeError: '<=' not supported between instances of 'str' and 'int'
Liste des noms d'hotel : []
```
### 2.2 Requête en écriture (45 min.)
Créer une requête permettant d'ajouter un nouveau client et de renvoyer son identifiant (c'est à dire son _numclient_). Si le client existe déjà (même nom ET même prénom), la méthode renverra son _numclient_ (on supposera, pour des raisons de simplification, qu'il n'y a pas clients homonymes). Pour cette requête, renseignez-vous sur la commande `INSERT INTO`. _Attention_ la clé primaire sera renseignée automatiquement, pas besoin de la stipuler. Notez également que l'attribut _curseur.lastrowid_ permet de récupérer le _numclient_ du nouveau client.
Créer une requête permettant d'ajouter un nouveau client et de renvoyer son identifiant (c'est à dire son _numclient_). Si le client existe déjà (même nom ET même prénom), la méthode renverra son _numclient_ (on supposera qu'il n'y a pas clients homonymes). Pour cette requête, renseignez-vous sur la commande `INSERT INTO`. _Attention_ la clé primaire sera renseignée automatiquement, pas besoin de la préciser explicitement. Notez également que l'attribut _curseur.lastrowid_ permet de récupérer le _numclient_ du nouveau client.
Vérifier que le nouveau client a bien été sauvegardé dans le fichier *Hotellerie.db* :
...
...
@@ -187,33 +283,91 @@ Vérifier que le nouveau client a bien été sauvegardé dans le fichier *Hotell
_Remarques_ :
-Pensez à quitter le logiciel `DB Browser` avant d'exécuter votre programme.
- Pensez à quitter le logiciel `DB Browser for SQLite` avant d'exécuter votre programme.
- En cas de destruction de la base `Hotellerie.db` (les requêtes en écriture sont toujours plus dangereuses que les requêtes en lecture !), pensez à la télécharger à nouveau !
------
<pclass=correction>
<b>Éléments de réponse pour les enseignants</b>
</p>
```python
importsqlite3
classHotelDB:
...
defadd_new_client(self,prenom,nom):
# On recherche si le client est là !
curseur=self.__conn.cursor()
try:
#curseur.execute("SELECT numclient FROM client WHERE nom='%s' \
# AND prenom='%s'" % (nom, prenom)) # obsolete
#curseur.execute("SELECT numclient FROM client WHERE nom=? \
# AND prenom=?", (nom, prenom)) # secure
curseur.execute("SELECT numclient FROM client WHERE nom='{}'\
AND prenom='{}'".format(nom,prenom))
exceptsqlite3.OperationalErrorassqlerr:
print('{} in {}'.format(str(sqlerr),self.__DBname))
returnNone
liste=curseur.fetchall()# pas d'homonyme dans le fichier
iflen(liste)!=0:
print('Le client existe déjà')
returnliste[0]
# on ajoute le client (la clé primaire est ajouté automatiquement)
curseur.execute("INSERT INTO client(nom, prenom) VALUES ('{}', '{}')".format(nom,prenom))
Dans cette dernière partie, nous vous invitons à imaginer et implémenter **DEUX (2)** requêtes originales à partir de la bdd *Hotellerie.db*, ou de tout autre bdd que vous aurez trouvée sur Internet.
Les résultats de vos requêtes devront faire l'objet d'une représentation graphique (graphe, histogramme, camembert...) en utilisant la librairie _Matplotlib_ (cf les remarques ci-dessous).
Les résultats de vos requêtes devront faire l'objet d'une représentation graphique (graphe, histogramme, camembert...) en utilisant la librairie _Matplotlib_ (cf remarques ci-dessous).
Vous pouvez _justifier_ la robustesse de vos requêtes en écrivant, dans votre programme principal, plusieurs appels à vos requêtes avec des paramètres farfelus, erronés... Bonus à ceux qui développerons une exception propre !
### Liens vers des Bdds `sqlite`
### Liens vers quelques Bdds `sqlite`
Voici quelques exemples de sites proposant des bdd _SQLite_ gratuites :
- Une base de données de films est étéchargeable en suivant [ce lien](https://1drv.ms/u/s!Ap1xZ3X70U50gvdIaHBrQ5uvSD-WSg?e=ncEblG). Elle est composée de 4 tables (acteur, réalisateur, film, distribution).
- Le site [SQLite tutorial](https://www.sqlitetutorial.net/sqlite-sample-database/) propose un base de données appelée _chinook_ (_digital media store_), composée de 11 tables.
- Dans le même genre, une base de données très célèbre : [Northwind](https://cs.ulb.ac.be/public/_media/teaching/infoh303/northwind_sqlite.db.zip)(8 tables).
- Si vous êtes fan des _Pokémon_, vous pouvez décompresser la base [veekun's Pokédex](http://veekun.com/static/pokedex/downloads/veekun-pokedex.sqlite.gz)(172 tables !).
- Si vous êtes fan de musique, vous pouvez décompresser et utiliser la base [musicBrainz](https://matthieu-moy.fr/cours/infocpp-S3/TPL/musicBrainz.zip)(4 tables).
- Une petite base concernant la [peinture](https://carnot.cpge.info/wp-content/uploads/2020/02/peinture.db).
- Beaucoup plus complexe : [murder-mystery](https://forge.univ-lyon1.fr/diu-eil/bloc4/-/raw/master/3_bases_de_donnees_introduction/TP/base-sql-murder-mystery.db)(9 tables). Lire le [site original](https://github.com/NUKnightLab/sql-mysteries).
-[postuler à Stanford](https://forge.univ-lyon1.fr/diu-eil/bloc4/-/raw/master/3_bases_de_donnees_introduction/TP/base-stanford.db)(3 tables).
> - Une base de données de films est téléchargeable en suivant [ce lien](https://1drv.ms/u/s!Ap1xZ3X70U50gvdIaHBrQ5uvSD-WSg?e=ncEblG). Elle est composée de 4 tables (acteur, réalisateur, film, distribution)
> - Le site [SQLite tutorial](https://www.sqlitetutorial.net/sqlite-sample-database/) propose un base de données appelée _chinook_ (_digital media store_), composée de 11 tables
> - Dans le même genre, une base de données très célèbre : [Northwind](https://cs.ulb.ac.be/public/_media/teaching/infoh303/northwind_sqlite.db.zip) (8 tables)
> - Si vous êtes fan des _Pokémon_, vous pouvez décompresser la base [veekun's Pokédex](http://veekun.com/static/pokedex/downloads/veekun-pokedex.sqlite.gz) (172 tables !)
> - Si vous êtes fan de musique, vous pouvez décompresser et utiliser la base [musicBrainz](https://matthieu-moy.fr/cours/infocpp-S3/TPL/musicBrainz.zip) (4 tables)
> - Une petite base concernant la [peinture](https://carnot.cpge.info/wp-content/uploads/2020/02/peinture.db)
> - Beaucoup plus complexe : [murder-mystery](https://forge.univ-lyon1.fr/diu-eil/bloc4/-/raw/master/3_bases_de_donnees_introduction/TP/base-sql-murder-mystery.db) (9 tables). Lire le [site original](https://github.com/NUKnightLab/sql-mysteries)
> - [Postuler à Stanford](https://forge.univ-lyon1.fr/diu-eil/bloc4/-/raw/master/3_bases_de_donnees_introduction/TP/base-stanford.db) (3 tables).
Vous pouvez également transformer des données du format `.csv` (_Comma Separated Value_) vers le format `.sqlite3`, en suivant ce [tutoriel video](https://tube.ac-lyon.fr/videos/watch/85399ea5-bba0-428b-9470-2d3bb41b7de1). Toutes les données de [data.gouv.fr](https://www.data.gouv.fr/fr/), par exemple, deviennent alors exploitables pour votre CR...
Vous pouvez également transformer des données du format `.csv` (_Comma Separated Value_) vers le format `.sqlite3`, en suivant ce [tutoriel vidéo](https://tube.ac-lyon.fr/videos/watch/85399ea5-bba0-428b-9470-2d3bb41b7de1). Toutes les données _open data_ de [data.gouv.fr](https://www.data.gouv.fr/fr/), par exemple, deviennent alors exploitables pour votre CR...
Au cas où vous opteriez pour une bdd originale, n'oubliez pas d'inclure cette base dans votre archive (si elle n'est pas trop volumineuse), et de préciser dans votre rapport le chemin pour la télécharger.
...
...
@@ -221,12 +375,14 @@ Au cas où vous opteriez pour une bdd originale, n'oubliez pas d'inclure cette b
Les deux requêtes attendues doivent être relativement sophistiquées (pas de simples `select XX from YY`).
- Si vous optez pour la bdd `Hotellerie.db`, n'hésitez pas à visiter un site de réservation d'hôtels pour trouver des idées de requêtes intéressantes. _Attention_ : le nom d'un hôtel n'est pas une clé primaire ! Plusieurs hôtels portent le même nom. Par contre, il n'existe pas 2 hôtels de même nom dans la même ville. Pensez-y !
- Si vous optez pour une autre base, développez une seconde classe dans un second fichier indépendant (sur le modèle de la classe **HotelDB**).
> - Si vous optez pour la bdd `Hotellerie.db`, n'hésitez pas à visiter un site de réservation d'hôtels pour trouver des idées de requêtes intéressantes. _Attention_ : le nom d'un hôtel n'est pas une clé primaire ! Plusieurs hôtels portent le même nom. Par contre, il n'existe pas 2 hôtels de même nom dans la même ville. Pensez-y !
> - Si vous optez pour une autre base, développez une seconde classe dans un second fichier indépendant (sur le modèle de la classe **HotelDB**).
**Remarques** :
- Vous programmerez les représentations graphiques dans le programme principal (et non pas dans la méthode qui traite la requête). En effet, quand on fait une requête sur une base de données, l'affichage graphique ne doit pas être obligatoire. C'est pour cela qu'on sépare la requête de l'affichage de son résultat (qu'il soit au format texte ou au format graphique).
> - Vous programmerez les représentations graphiques dans le programme principal (et non pas dans la méthode qui traite la requête). En effet, quand on fait une requête sur une base de données, l'affichage graphique ne doit pas être obligatoire. C'est pour cela qu'on sépare la requête de l'affichage de son résultat (qu'il soit au format texte ou au format graphique).
> - Usage de _Matplotlib_ : À titre d'exemples, vous trouverez, à côté de cet énoncé, un fichier nommé [ex_matplotlib.py](./ex_matplotlib.py). L'exécution de ce script génère 4 figures dans le sous-répertoire *figures*. Inspirez-vous largement de ce programme pour vos propres figures.
- Usage de _Matplotlib_ : À titre d'exemples, vous trouverez, à côté de cet énoncé, un fichier nommé [ex_matplotlib.py](./ex_matplotlib.py). L'exécution de ce script génère 4 figures dans le sous-répertoire *figures*. Inspirez-vous largement de ce programme pour vos propres figures.*Conseil*: Évitez de vous lancer dans des requêtes avec des données géographiques, genre `trouver tous les hôtels à moins de 5 kilomètres` car l'usage de cartes géographiques dépasse les attentes de ce qui est demandé ici.
> - *Conseil*: Évitez de vous lancer dans des requêtes avec des données géographiques, genre `trouver tous les hôtels à moins de 5 kilomètres` car l'usage de cartes géographiques dépasse les attentes de ce qui est demandé ici.