From 7b52d0611223279969c50bbc342f8951a47d44f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Derrode?= <stephane.derrode@ec-lyon.fr> Date: Sun, 28 Feb 2021 20:47:25 +0100 Subject: [PATCH] update BE #3 --- seance3_4h/consignes_BE#3.md | 21 +++-- seance3_4h/seance3_4h.md | 152 ++++++++++++++++++++++++----------- 2 files changed, 121 insertions(+), 52 deletions(-) diff --git a/seance3_4h/consignes_BE#3.md b/seance3_4h/consignes_BE#3.md index 49a811e..0e49322 100644 --- a/seance3_4h/consignes_BE#3.md +++ b/seance3_4h/consignes_BE#3.md @@ -1,17 +1,26 @@ ### Consignes pour le rendu (BE #3 - INF-TC2) -Ce BE est le premier devoir à rendre concernant INF-TC2. Le compte-rendu (CR) de ce travail devra être déposé sur ``Pedagogie1``, sur l'espace de dépôt spécifique à votre groupe. Et cela dans un **délai de deux semaines après la séance** (délai de rigueur, aucun travail accepté au delà de cette date). +Ce BE est le premier devoir à rendre concernant INF-TC2. Le compte-rendu (CR) de ce 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`. **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) contenant l'ensemble des fichiers suivant : - - La base de données *Hotellerie.db*, après exécution de vos requêtes. + - Le dépôt consistera en une unique archive (zip, rar) 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 dernière base de données de votre choix : 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. - - un rapport (format _word_, _pdf_, ou mieux encore _markdown_ !) contenant + - 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. + - 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-devoir1.zip* ou *nom1-nom2-devoir1.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 axes...) + - **Qualité de l'API** : choix des noms et arguments des méthodes, pas de requêtes SQL hors de la classe, affichages textuel ou graphique 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 par des exceptions. + - **Qualité des requêtes** : originalité et justification de l'intérêt, difficulté technique, valorisation dans le rapport. \ No newline at end of file diff --git a/seance3_4h/seance3_4h.md b/seance3_4h/seance3_4h.md index abfe1f5..c48b969 100644 --- a/seance3_4h/seance3_4h.md +++ b/seance3_4h/seance3_4h.md @@ -2,37 +2,35 @@ [[_TOC_]] -# BE #3 : Base de données SQL +# BE #3 : Exceptions et Base de données SQL - - -L'objectif de ce BE est d'expérimenter la manipulation (créer, lire, interroger, modifier) de bases de données relationnelles avec le langage SQL (*Structured Query Language*), à partir de _Python_. Ce BE est décomposé en trois parties: +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: 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. L'énoncé est rédigé sous une forme tutoriel; - 1. **La troisième partie** (durée: 120 min. et +) vous amène à un travail plus personnalisé, pour mettre à profit vos connaissances sur la gestion des exceptions, la librairie graphique *matplotlib* et bien sûr les bases de données _SQL_. + 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 bien sûr 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é). +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. --- ## 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 fonctionne en stockant une base de données dans un fichier d'extension _.sqlite_. La base de tests utilisée 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 qui sera utilisé durant ce BE est _SQLite_. Ce système très simple fonctionne en stockant 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 : -<center><img src="figures/schema_bdd_hotellerie.png" style="width:60%"/></center> +<center><img src="figures/schema_bdd_hotellerie.png" style="width:70%"/></center> -La base est composée de 5 tables, ces tables étant composées d'un nombre variable de champs. Les champs soulignés représentent les clés primaires (ou *primary key (PK)* en anglais). En particulier, la clé primaire de la table **chambre** est composée des attributs *numchambre* et *numhotel* (cette dernière étant la clé primaire de la table **hotel**). +La base est composée de 5 tables, ces tables étant composées d'un nombre variable de champs. Les champs soulignés représentent les clés primaires (ou *primary key (PK)* en anglais) de chaque table. En particulier, la clé primaire de la table **chambre** est composée des attributs *numchambre* et *numhotel* (cette dernière étant la clé primaire de la table **hotel**). -*Remarque* : Vous trouverez [ici, une vidéo](https://www.youtube.com/watch?v=VgTRNqn2fn0) qui montre comment utiliser [draw.io](https://app.diagrams.net) pour créer un diagramme entité-association. Il NE vous est PAS demandé de dessiner le diagramme de cette base de données. +*Remarque* : Vous trouverez [ici](https://www.youtube.com/watch?v=VgTRNqn2fn0) une vidéo qui montre comment utiliser [draw.io](https://app.diagrams.net) pour créer un diagramme entité-association. Il NE vous est PAS demandé de dessiner le diagramme de cette base de données. -### 1.1 DB browser for SQLite (30 min.) +### 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... - - Téléchargez et installez [DB Browser for SQLite](https://sqlitebrowser.org/) en suivant les instructions d'installation en fonction de votre système d'exploitation. - - 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). + - 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). <center><img src="figures/DBBrowser.png" style="width:75%"/></center> @@ -47,7 +45,6 @@ La réponse apparaît sous forme de 12 lignes. Ça vous rappelle des choses ? Si - et tant d'autres... - ### 1.2 Quelques requêtes en _Python_ (15 min.) **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). @@ -59,12 +56,15 @@ Le squelette typique d'un tel programme s'écrit : import sqlite3 if __name__ == '__main__': conn = sqlite3.connect('hotellerie.db') # connexion à la bdd + # travail sur la bdd à partir du connecteur + ... + conn.commit() # pour enregistrer les éventuels changements conn.close() # pour fermer proprement l'accès à la base ``` -Ainsi, pour obtenir la réponse à la requête précédente : +Voici le code _Python_ permettant d'obtenir la réponse à la requête précédente : ```python import sqlite3 if __name__ == '__main__': @@ -72,15 +72,18 @@ 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() # 1ère ligne résultat de la requête + ligne1 = curseur.fetchone() # recupère la 1ère ligne du résultat de la requête print('ligne1 =', ligne1) - ligneAll = curseur.fetchall() # toutes les lignes de la requête + ligneAll = curseur.fetchall() # recupère toutes les lignes du résultat de la requête print('ligneAll =', ligneAll) conn.close() ``` -_Remarque_ La commande ```conn.commit()``` n'est pas nécessaire ici puisque le script ne modifie pas la base. +_Remarques_ + + - La commande ```conn.commit()``` n'est pas nécessaire ici puisque le script est une requête en lecture ; elle ne modifie donc pas la bdd. + - Notez que la méthode ```fetchone()``` retire le résultat de la liste des résultats. Pour preuve: ce résultat n'est pas présent suite à l’affichage du ```fetchall()```. Copiez et exécutez ce programme ; le résultat se présente sous forme d'un tuple, ou sous forme d'une liste de tuples. Ainsi la commande suivante imprime le nom du premier hôtel qui apparaît dans la liste des résultats de la requête : ```python @@ -100,72 +103,129 @@ if __name__ == '__main__': conn.close() ``` +### 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 : + + - la base de données est introuvable; + - le nom des tables 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 : + +```python +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 Exception as err: # interception d'une exception quelconque + print('type exception: %s' % (type(err).__name__)) + print('%s' % (str(err))) + finally: # fermeture de la base dans tous les cas + conn.close() +``` + +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. + +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. + +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__). + + --- ## 2. Classe HotelDB (75 min.) -Il s'agit de créer une classe **HotelDB** permettant de réaliser un certain nombre de requêtes et de mises à jour de la base *Hotellerie.db*. +Dans cette partie, nous allons créer un classe **HotelDB** permettant de réaliser des requêtes de lecture et de mises à jour de la base *Hotellerie.db*. + ### 2.1 Requête en lecture (30 min.) Dans un fichier *HotelDB.py*, commencez à développer la classe permettant de répondre au programme principal suivant, dont l'objectif est d'afficher le nom des hôtels 2 étoiles (notez que le nombre d'étoiles est passé en argument) : - ```python - if __name__ == '__main__': - aHotelDB = HotelDB('hotellerie.db') - resultat = aHotelDB.get_name_hotel_etoile(2) - print("Liste des noms d'hotel 2 étoiles : ", resultat) - ``` +```python +if __name__ == '__main__': + aHotelDB = HotelDB('hotellerie.db') + nbEtoiles = 2 + resultat = aHotelDB.get_name_hotel_etoile(nbEtoiles) + print("Liste des noms d'hotel", nbEtoiles, "étoiles : ", resultat) +``` -Il s'agit ici de développer le constructeur (qui stockera le connecteur en tant qu'attribut) et la méthode _get_name_hotel_etoile(...)_. +Le constructeur (méthode *\_\_init\_\_(...)*) se chargera d'ouvrir une connexion vers la bdd, alors que le destructeur (méthode *\_\_del\_\_(...)*, _cf_ remarque ci-dessous) se chargera de la fermer. -*Remarque* : Pour fermer correctement l'accès à la base de donnée, pensez à implémenter la méthode ```__del__(self)``` (vue en cours), qui est appelée automatiquement par _Python_ lors de la destruction des objets de la classe. Typiquement : +_Remarques_ : + + - Pour fermer correctement l'accès à la base de donnée, pensez à implémenter la méthode *\_\_del\_\_(...)* (vue en cours), qui est appelée automatiquement (et de manière implicite) par _Python_ lors de la destruction des objets de la classe. Typiquement : ```python def __del__ (self): self.__conn.close() ``` + - La méthode `get_name_hotel_etoile(...)` se charge de retourner le résultat de la requête, mais ne se charge pas d'en afficher le résultat. L'affichage est laissé au programme principal, ici essentiellement pour contrôler le résultat. + - Pensez à intercepter les exceptions de type `sqlite3.OperationalError`, comme vu précédemment, et à renvoyer dans ce cas un résultat sous la forme d'une liste vide. + +__Améliorations à implémenter__ : + + - 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. -__Améliorations à implémenter__ : Comment se comporte votre programme si on insère la commande `aHotelDB.get_name_hotel_etoile(-1)` ? Et la commande `aHotelDB.get_name_hotel_etoile("Hello")`. Comment éviter que cet usage ne produise la fin brutale du programme et qu'il renvoie une liste vide tout simplement ? ### 2.2 Requête en écriture (45 min.) -Créer une requête permettant d'ajouter un nouveau client. Si le client existe déjà (même nom ET même prénom), la méthode renverra le numéro de ce client. Si le client n'existe pas, alors la méthode créera un nouveau client, en lui adjoignant le premier numéro non encore utilisé (c'est une clé primaire !). Pour cela, renseignez-vous sur la commande ``INSERT INTO``. Vérifier que le nouveau client a bien été sauvegardé dans le fichier *Hotellerie.db* : +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. + +Vérifier que le nouveau client a bien été sauvegardé dans le fichier *Hotellerie.db* : - soit en consultant la base avec `DB Browser for SQLite`; - - soit en exécutant par 2 fois le même programme, vous devriez retrouver le même numéro de client. + - soit en exécutant par 2 fois successives le même programme ; vous devriez alors retrouver le même numéro de client. + +_Remarques_ : + + - Pensez à quitter le logiciel `DB Browser` 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 ! + --- ## 3. Requêtes libres (120 min. et +) -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. Voici quelques exemples de sites proposant des bdd _SQLite_ gratuites : +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). + +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` + +Voici quelques exemples de sites proposant des bdd _SQLite_ gratuites : - 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 abse de données très célèbre [Northwind](https://cs.ulb.ac.be/public/_media/teaching/infoh303/northwind_sqlite.db.zip) (8 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 : [peinture.db](https://carnot.cpge.info/wp-content/uploads/2020/02/peinture.db) - - [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). + - 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... 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. -**Quelques conseils**: - -* Si vous optez pour la bdd `HotelDB`, 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évelopper une seconde classe dans un second fichier (sur le modèle de la classe **HotelDB**). +### Quelques conseils -Les deux requêtes attendues doivent être relativement sophistiquées (pas de simples ``select ... from``), l'évaluation de cette partie dépendra : +Les deux requêtes attendues doivent être relativement sophistiquées (pas de simples ```select XX from YY```). - 1. de l'originalité de vos requêtes, - 1. de la valorisation graphique des résultats de vos requêtes à l'aide de la librairie **matplotlib**, - 1. de la robustesse de vos requêtes à des usages erronés ou inattendus (usage des exceptions). _Par exemple_ : comment se comporte la requête si le nom de l'hôtel passé en argument n'existe pas dans la base ? Vous pouvez "prouver" la robustesse de vos requêtes en proposant une série de _crash-tests_. - 1. de leur présentation dans le rapport. + - 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**). -Votre programme principal doit contenir plusieurs appels à chaque requête (en changeant les arguments), de manière à illustrer leur robustesse dans des cas de figure différents (_p. ex._ hôtel ou ville inconnue). **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). -- 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. +- 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. -- GitLab