Étiquette : SQL

Projets

Binnerto ©, un système de gestion de bases de…

Il y a quelque mois de cela, nous vous avions laissé bien pantois. En effet, sur le site Binnerto©, un site très qualitatif de produits geek, il était jusque-là impossible de commander le moindre petit produit, et nous en sommes grandement désolés.

Mais aujourd’hui est un jour nouveau, nous venons de sortir du sable, enfin !

Vous allez désormais (dans un temps prochain (soumis à condition et à l’acceptation du crédit par votre banque)) pouvoir acheter (dans la limite des stocks disponibles et des vendeurs disponibles pour réaliser les commandes) tous les produits vendus par notre site dès maintenant grâce à un SGBDR !

Quelques petits rappels utiles et informations nécessaires à la compréhension

Vous vous demandez bien qu’est-ce qu’un SGBDR et à quoi cela peut bien servir pour commander votre produit préféré (et fort bien utile). Et bien pour une fois cette acronyme est en français (ou DBRMS dans la langue de Shakespeare), il signifie littéralement Système de Gestion de Bases de Données Relationnelles. Euuuuh quèsaco ? En fait c’est un logiciel qui permet de mettre en relation différents fichiers contenant de nombreuses données. Voyons à quoi cela ressemble dans notre cas, cela apportera plus de clarté.

Imaginons que vous vous appeliez Antonin Kroyable et que vous ayez commandé une Souris révolutionnaire pour votre tout nouvel appareil à la poire le 14 février 2022. Et bien le SGBDR ; nommons le affectueusement S&Co pour la suite pour plus de simplicité et de proximité, permet de lier trois fichiers pour les unifier. Dans un fichier vous trouverez vos informations personnelles, ce sera le fichier « client », dans un second, vous aurez toutes les informations sur cette souris, c’est-à-dire dans le fichier « produit ». Et pour la commande que vous avez faites ? Et bien elle se trouve dans le fichier « commande » évidemment. Tout le travail du S&Co et donc de lier ces trois fichiers pour avoir les informations les plus utiles pour chaque besoin, c’est-à-dire pour vous clients, et pour nous gestionnaires du site et responsables des stocks et livraisons produits, sans avoir des fichiers de 3 Km de long avec toutes les informations, dont certaines inutiles à certains mais utiles à d’autres.

Rentrons maintenant dans le cœur de la bête pour mieux l’appréhender.

Il faut savoir que ce S&Co, étant user-friendly, a une interface utilisateur (ou UI en anglais) graphique, permettant une meilleure expérience utilisateur (ou UX encore une fois en anglais), en limitant de perdre ce dernier, qui n’a pas forcément les connaissances nécessaires pour affronter la peur d’un terminal sombre et peu ragoûtant. Il est au moins rassuré, mais cela nous complexifie bien la tâche, comme nous allons le voir.

Dans cette quête de la rose-amère de la perfection du design, ce S&Co ne fera que pâle figure car étant codé extrêmement en Python avec comme interface graphique Tkinter, les possibilités de design sont amoindries comparé à d’autres langages mieux prévus pour les interfaces graphiques. Nonobstant, vous avez quand-même un produit a minima fini.

Il y aura donc, vous l’aurez compris deux parties majeures pour appréhender ce S&Co, la partie graphique, en Tkinter, qui n’est que la rustine de ce qui fait tourner la machine, ce qui est à l’intérieur, c’est-à-dire les fonctions mères pour mettre en relation SQL (le langage qui permet de parler aux tables de données ; le charmeur de données), et Python.

Le vif du sujet SQL-Python

Sans plus de divagations, voyons d’où tout est parti : SQLite. En effet, il est impossible de discuter directement avec les données en Python, ou alors cela serait très complexe.

Pour plus de facilité, on utilise à la place un autre langage, le SQL (qui est d’une facilitée incroyable en comparaison). Mais pour lier un programme Python avec du SQL il faut une bibliothèque, et c’est la que SQLite vient nous sauver ! En effet on peut rentrer des commandes SQL dans une fonction Python et avoir tous les bénéfices du SQL en ayant les retours en Python pour être réutilisés, c’est presque magique !

Réalisation des trois bases de données dans un fichier CSV

Pour commencer avec des données à chaque démarrage sans partir de rien, nous avons créé 3 fichiers CSV (où les éléments sont séparés par des virgules) que l’on récupère ensuite pour l’importer (on verra cela plus tard). Donc, comme on l’a dit plus tôt, 3 tables seront liées, il y aura donc 3 fichiers CSV qui contiendront quelques éléments pour chaque table (environ une dizaine).

Tout d’abord, il y a la table PRODUITS, dans le fichiers produits.csv. Dans cette table on présente comme suit les principales caractéristiques du produits :

id_produitnom_produitprixstock
6Binnerto en acier0.001

Toutes les données sont présentées comme cela sauf, que d’une colonne à l’autre, c’est un virgule qui les sépare. L’id_produit est un identifiant unique permettant à coup sur de retrouver le produit sans risque de se tromper (par exemple si 2 produits ont un nom identique).

Il en est de même pour la table CLIENTS sur le fichier clients.csv :

id_clientnomprenomadresse_mailadressedate_naissancedate_inscriptionvip
8KroyableAntoninantonin.kroyable@lol.mdr13 chemin des Rippes, Salle-à-Sac10-03-200711-11-2011True

De la même manière, id_client est l’identifiant unique qui correspond au client.

Passons maintenant à la dernière table, la plus importante, la table COMMANDES, dans le fichier commandes.py :

id_commandeid_client_commandeid_produitdate_commandedate_livraisonenvoyequantiteprix_total
308614-02-202219-02-2022True30.00

Encore une fois, id_commande est l’identifiant unique de la commande mais, il y a une petite subtilité encore. En effet, on va lier les deux autres tables à celle-ci avec 2 attributs : id_client_commande et id_produit, qui correspondent respectivement à la ligne de chaque table. Ainsi, avec une moindre écriture, on arrive à avoir toutes les informations pertinentes sur une seule ligne !

CRUD, la clé de voûte du projet

CRUD, de l’anglais Create, Read, Update, Delete, a été notre expression clé durant ce projet, notre guide suprême, car pour un S&Co, cela les 4 points les plus importants. Il faut pouvoir Créer des tables et des éléments dans ces tables, les Lire, les Mettre-à-Jour, mais aussi les supprimer. Voyons comment cela a pu être réalisé en Python/SQL.

1. Create

Deux choses sont à créer dans ce projet, les tables, que ce soit PRODUITS, CLIENTS ou encore COMMANDES, mais aussi les éléments qui composent ces tables, les lignes si vous vous rappelez de nos tableaux de tout-à-l’heure.

Pour créer une table, rien de plus simple comme on le voit ici avec la création de la table PRODUITS :

def create_table_produits():
    curseur.execute("CREATE TABLE IF NOT EXISTS PRODUITS(id_produit INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, nom_produit TEXT, prix NUMERIC, stock INTEGER)")
    connection.commit() #On envoie la modif dans la base de données

Le SQL est ce qui est dans la méthode .execute()

Cette fonction permet donc de créer la table PRODUITS, en vérifiant bien sûr qu’elle n’existe pas pour éviter les erreurs. Si vous vous rappelez de l’id_produit de tout à l’heure, qui permet de retrouver avec certitude un produit, et bien en SQL, c’est une PRIMARY KEY, soit une clé primaire ; une sorte de numéro d’identité du produit (qui est donc unique).

Mais on peut aussi avoir besoin de créer un enregistrement, comme suit :

def add_produits(produits_ajout):
    curseur.executemany("INSERT INTO PRODUITS(nom_produit, prix, stock) VALUES (?,?,?)", produits_ajout)
    make()

En vérifiant bien sûr que le tuple en entrée soit composé de 3 éléments.

2. Read

Pour lire, ça se corse, on peut vouloir lire tout ou partie, il a donc fallu faire des choix en réfléchissant à chaque fois au besoin le plus utile de lecture.

Ainsi on peut tout lire comme ici :

def select_all_produits():
    curseur.execute("SELECT * FROM PRODUITS")
    return [("id_produit", "nom_produit", "prix", "stock")] + curseur.fetchall()

On retourne en plus les noms des attributs afin de pouvoir les afficher comme il faut dans le tableau en Tkinter

Mais aussi comme cela :

def select_all_commandes():
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client")
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

Ici, on fait une double jointure pour afficher à la place de l’id_client et de l’id_produit, le nom du client et du produit pour plus de clarté.

En jouant avec le SQL, il y a aussi des fonctions plus spécifiques comme celle qui suit permettant d’afficher toutes les commandes d’un même client :

def toutes_commandes_clients(id_client):
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client WHERE id_client_commande=?",(id_client,))
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

3. Update

Pour mettre à jour, nous nous sommes demandé ce que nous aurions le plus besoin de mettre à jour dans chaque enregistrement de chaque table, et pour cela nous avons pour chaque table créé une fonction update qui change uniquement l’élément qui a le plus besoin d’être changé :

def update_commandes(id_commande, envoye: bool):
    curseur.execute("UPDATE COMMANDES SET envoye=? WHERE id_commande=?", (envoye, id_commande))
    make()

On change ici l’état de la commande avec un Booléen (Vrai/Faux), car c’est la commande que l’on est le plus amenés à réaliser.

4. Delete

Pour la suppression, afin d’être sûr de ne pas faire d’erreurs, cette dernière se base uniquement sur l’id unique de chaque enregistrement dans une table.

Ainsi :

def delete_clients(id_client):
    curseur.execute("SELECT * FROM COMMANDES WHERE id_client_commande=?",(id_client,))
    temp = curseur.fetchall()
    if str(temp) == "[]":
        curseur.execute("DELETE FROM CLIENTS WHERE id_client=?", (id_client,))
    make()

Ici, on supprime un produit en fonction de son id_produit, tout en vérifiant qu’il n’est pas utilisé dans la table commande, sinon cela pourrait poser problème.

Un affichage graphique : Tkinter

CComme dit plutôt, il faut bien interagir avec toutes ces bases de données si complexes (qu’on appellera BDD), et ça avec une interface graphique en Tkinter. Après avoir réfléchi à plusieurs croquis de ce que pourrait être l’UI (dont on vous ne donnera pas d’exemple pour ne pas nous faire voler nos maquettes), on a réussi à trouver l’UI la plus simple pour nos futurs employés, qui devront se servir de cette interface pour gérer les commandes, pendant que nous serons à Ibiza en train d’empocher un maximum d’argent.

Tous les produits de chez Binnerto en un clic

N’oublions pas le maître-mot de tout ce que l’on peut produire : LA FACILITÉ ! Il est donc inutile lorsqu’on veut sélectionner un produit d’écrire chaque lettre de ce dernier (sans oublier les potentielles erreurs de frappes ou maladresses). Nous avons donc opter pour une liste des produits qu’il suffit de sélectionner. Avec Tkinter, l’outil se nomme « Combobox » et c’est bien pratique.

from tkinter import *
from tkinter import ttk

def action(recup):
    #on appelle la fonction qui lie Python et SQL pour interagir avec la TABLE

listeproduits = ["Parpaing externe", "Clé USHellB", "Clavier Pasteur STL", "Voiture à Hydrogène", "CPU Nucléaire Leif", "Bouton d'allumage automatique", "Binnerto en acier", "Souris révolutionnaire"]
choix_prod = Label(racine, text = "Enlever Stock :")
choix_prod.place(x=300, y=130)
aff_prod = ttk.Combobox(racine, state = "readonly", values = listeproduits)
aff_prod.bind("<<ComboboxSelected>>", action)
aff_prod.place(x=265, y=170)

Comme une bonne nouvelle n’arrive jamais seule, certes l’outil « Combobox » est parfait pour ce que nous voulons faire, mais il n’est pas inclus lors de l’importation de Tkinter *. Il faut donc le rajouter à part, au péril d’un peu plus de mémoire. 

C’est bien beau de pouvoir sélectionner un produit dans une liste, mais il faut bien le récupérer pour pouvoir l’exploiter un peu. Heureusement, la méthode « .get() » permet de récupérer le choix fait par l’utilisateur, et à l’aide de la fonction ci-dessus qui affiche ce choix, on est sûr d’avoir bien récupéré le produit. Maintenant que l’on peut sélectionner un produit, il ne reste plus qu’à décider s’il faut le retirer du stock en cas de commande, ou l’ajouter en cas de fabrication (même si ce dernier cas n’arrive jamais 🤫).

Un accès aux commandes plus pratique

Nous suivons avec soin chaque commande pour la meilleure satisfaction de nos clients. C’est pour cela qu’il faut savoir à l’aide du numéro de commande : les produits qu’elle contient, le nom et prénom du client, le jour de l’envoi, si le colis a été reçu, etc…

Il faut donc qu’à l’aide de « id_commande », on trouve toutes ces informations, et cela, de façon la plus lisible possible… et pourquoi pas un tableau ?! Mais il faut d’abord récupérer le numéro d’une commande saisit dans un input :

def num_commande(event):
	n_fen = Toplevel(racine)
    t = Tableau(n_fen, select_client_specifique(int(entree.get()))) #on ouvre une nouvelle fenêtre avec le tableau représentant les données

def valide(test):
    return test.isdigit()

com_liv = Label(racine, text = "Afficher la commande d'un client (rentrez son ID)")
com_liv.place(x=900, y=130)
choix_com = racine.register(valide)
value = StringVar(racine)
entree = Entry(racine, textvariable = value, width = 30, validate = 'key', validatecommand = (choix_com,'%S'))
entree.place(x=865, y=170)
entree.bind("<Return>", num_commande)

Comme pour les produits, on récupère le numéro de la commande à l’aide de la méthode « .get() », mais cette fois-ci, uniquement lorsque nous validerons en appuyant sur la touche « Entrée » du clavier. Mais imaginons que lorsque notre employé utilise cette interface, il s’endorme sur le clavier ! Malheur ! Il faut redoubler de sécurité. C’est pour cela que dans cet input, on peut seulement y renseigner des chiffres, et cela, grâce à la fonction « valide » qui vérifie à chaque saisie si celui la est un chiffre.

Le Tkinter a pu grandement avancer grâce au tutoriel d’Aël D. sur Tkinter

Vous trouverez ci-dessous le projet, qui est en constante évolution :

Projets

Système de gestion de base de données relationnelle sur…

Venez découvrir tout au long de ce compte rendu comment nous avons créé un programme en python permettant à l’utilisateur d’interagir avec la base de donnée que nous avons créé.

Dans un premier, on a importé toutes les bibliothèques nécessaires à la création du programme.

from tkinter import *
from tkinter import ttk
import tkinter as tk
import sqlite3
import pandas
import os

La bibliothèque « tkinter » va servir à créer l’interface graphique ( vous pouvez en apprendre plus grâce à ce tutoriel, créé par Aël, élève en Spé NSI en classe de 1ère ).

La bibliothèque sqlite3 permet d’interagir avec une base de donnée.

La bibliothèque pandas permet de récupérer des données dans un ficher csv.

Et enfin la bibliothèque os permet de supprimer des fichiers dans le cadre de ce programme.

I. Création de la page d’accueil

Nous avons d’abord réfléchi à l’interface graphique que nous souhaitions réaliser.

Il nous fallait d’abord une page d’accueil, avec quatre boutons permettant d’effectuer les quatre fonctions nécessaires à l’utilisation d’une base de données : créer la base, lire la base, modifier la base, et enfin supprimer la base.

Le code ci-dessous permet de créer la page d’accueil, contenant un titre et 4 boutons.

window = Tk() # page d'accueil avec les 4 boutons

window.title("Gérer une base de donnée sur les jeux vidéos")
window.geometry("1280x1024")
window.maxsize(1280,1024)
window.config(bg="#7EBDC2")
window.iconbitmap("manette.ico")

titre = Label(window, text="4 boutons pour intéragir avec la base de données", font=("Consolas",20), bg="#d1fdff", fg="black")
titre.pack(pady=45)

boite = Frame(window, bg="#7EBDC2")
boite.pack(pady=200)

boutonC = Button(boite, text="CREATE", font=("Consolas",15), bg="white", fg="black", command=create, width=10, height=3)
boutonC.grid(row = 1, column = 0, padx = 15, pady=15)

boutonR = Button(boite, text="READ", font=("Consolas",15), bg="white", fg="black", command=read, width=10, height=3)
boutonR.grid(row = 1, column = 1, padx = 15, pady=15)

boutonU = Button(boite, text="UPDATE", font=("Consolas",15), bg="white", fg="black", command=update, width=10, height=3)
boutonU.grid(row = 3, column = 0, padx = 15, pady=15)

boutonD = Button(boite, text="DROP\n DATABASE", font=("Consolas",15), bg="white", fg="black", command=delete, width=10, height=3)
boutonD.grid(row = 3, column = 1, padx = 15, pady=15)

window.mainloop()

Visuel graphique :

II. Comment utiliser nos fonctions

Lier une interface graphique à des fonctions nécessite l’emploi de boutons.

Il nous fallait donc créer 4 fonctions permettant d’interagir avec la base de données.

A. La fonction create()

Nos fichiers à exploiter sont au format .csv. Avec la bibliothèque pandas, nous avons stockés les fichiers csv dans des variables pour pouvoir les exploiter.

ed = pandas.read_csv("relation_editeur.csv")
jv = pandas.read_csv("base_principale.csv")

Puis nous avons dû créer un fichier au format .db (Data Base).

connection = sqlite3.connect('base.db')
curseur = connection.cursor()

Nous avons créé les deux relations en indiquant les attributs et leur type.

curseur.execute("CREATE TABLE IF NOT EXISTS ED(Id_Editeur INTEGER, Editeur TEXT, PRIMARY KEY(Id_Editeur AUTOINCREMENT))")
curseur.execute("CREATE TABLE IF NOT EXISTS JV(Id_Jeu INTEGER, Jeu TEXT, Annee INTEGER, Prix FLOAT, Plateforme TEXT, Id_Editeur INTEGER, PRIMARY KEY(Id_Jeu AUTOINCREMENT), FOREIGN KEY(Id_Editeur) REFERENCES ED(Id_Editeur))")

Et enfin, nous avons créé deux boucles « for » pour ajouter les enregistrements des fichiers .csv dans la base de données automatiquement.

for i in range(len(ed)):
    data = [ed.loc[i, "Editeur"]]
    curseur.execute("INSERT INTO ED(Editeur) VALUES (?)", data)
    
for i in range(len(jv)):
    data = []
    data2 = list(jv.loc[i, ["Jeu", "Annee", "Prix", "Plateforme", "Id_Editeur" ]])
    data2[1] = int(data2[1])
    data2[4] = int(data2[4])
    data.append(tuple(data2))
    curseur.executemany("INSERT INTO JV(Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?, ?, ?, ?, ?)", data)

Pour finir, nous avons mis chacun de ces éléments dans une seule fonction : create(), appelée par le premier bouton de l’accueil. Nous avons décidé d’afficher une phrase pour indiquer à l’utilisateur que la base de données a bien été créé. Un simple Label nous a suffit (allez voir le tutoriel d’Aël pour plus de détails).

B. La fonction drop_database()

En SQL, la commande « DROP DATABASE » permet de supprimer la base de données. En python, il est impossible d’utiliser cette commande. On a donc importé le module os qui permet dans ce cas de supprimer un fichier.

Ainsi, la structure de la fonction drop_database() serait :

os.remove("base.db")

Cependant, si le fichier n’existe pas, tenter de le supprimer affichera, dans l’IDE python, un message d’erreur. Pour éviter ce problème, nous avons créé une variable globale « test » qui prend la valeur 0 si la base n’existe pas, et 1 si la base existe. Nous avons donc testé avec un « if » la valeur de « test », et le fichier ne sera supprimé que si « test » est égal à 1 et un message de confirmation s’affichera, sinon un message apparaît sur la fenêtre tkinter pour indiquer que la base n’existe pas, ce qui nous donne la fonction suivante.

def drop_database(): # supprime le fichier de la base de donnée s'il existe
    global test
    global message
    global texte
    
    if test == 1:
        os.remove("base.db")
        test -= 1
        supmes()
        message = "La base de donnée a bien été suprimée"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()
    elif test == 0:
        supmes()
        message = "La base de donnée n'existe actuellement pas"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()

C. La fonction read()

Pour la santé mentale de tout lecteur, nous ne montrerons pas ici l’intégralité de notre fonction (sinon, le fichier entier est téléchargeable en bas de page).

En SQL, pour sélectionner des éléments d’une base de données, on utilise la commande « SELECT Attribut_1, Attribut_2, … FROM DB ». En python, avec la bibliothèque sqlite3, la structure est exactement la même. Or il faut récupérer ce que contient le curseur et le stocker dans une variable, pour ensuite l’afficher. Par exemple, pour afficher tous les attributs d’une relation dans la console de l’IDE Python, on utilise :

curseur.execute("SELECT * FROM RELATION")
lecture = curseur.fetchall()#récupère les données obtenues par la ligne 1 en 'str'
print(lecture)

Lorsque l’on clique sur le bouton « read » de la page d’accueil, une nouvelle fenêtre tkinter apparaît. Sur cette fenêtre, on peut choisir de lire la relation principale (relation Jeux Vidéo), la relation secondaire (relation Editeurs) ou de retourner au menu, soit à l’accueil.

Le bouton « Retour Menu » supprime simplement la fenêtre, avec la commande tkinter :

window_r.destroy()

La lecture de la relation Editeurs est assez simple. En effet, la relation Editeurs ne contient que deux attributs : la clé primaire « Id_Editeur » et le nom de l’éditeur, « Editeur ». On a donc choisi de montrer par défaut toute la relation dans la même fenêtre que celle contenant les boutons de lecture, et donc supprimer les boutons. Nous avons donc dû globaliser nos boutons, afin de les supprimer ainsi :

global bouton
bouton.pack_forget()

Enfin, il a fallu sélectionner tous les éléments et les afficher dans un Label.

curseur.execute("SELECT * FROM ED")#fonction simplifiée
affichage = curseur.fetchall()
affiche = Label(text=affichage)#plus de détails sur le tutoriel
affiche.pack()

Pour finir, il nous a fallu afficher le contenu de la relation principale selon les demandes de l’utilisateur.

Pour cela, nous avons créé, grâce à des recherches supplémentaires sur internet, des checkbutton (des cases à cocher), qui peuvent être sélectionnés ou non pour choisir si l’on affiche l’attribut.

De plus, nous avons permis à l’utilisateur de choisir de spécifier sa demande avec la commande SQL « WHERE Attribut = Valeur ». Cela nous permet donc de faire une sélection très précise d’éléments à afficher.

D. La fonction update()

La dernière fonction à créer !

Modifier la base de données implique 3 actions : ajouter un enregistrement, supprimer un enregistrement, et modifier une valeur d’un des attributs de l’enregistrement.

Nous avons décidé qu’il était impossible, grâce à l’interface, d’ajouter directement un éditeur dans la relation « Editeur ». Ainsi lors de l’ajout ou de la modification d’un enregistrement de la relation Jeux Vidéo, si l’éditeur renseigné n’existe pas dans la relation correspondante, il y est créé. Cet ajout se fait simplement :

#Selon les spécificités de la bibliothèque sqlite3, avec "Editeur" renseigné par l'utilisateur
curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
temp = curseur.fetchall()
if temp == [] : #si l'éditeur n'existe pas
  curseur.execute("INSERT INTO JV (Editeur) VALUES (?)",(Editeur,))
  curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
  id_editeur = curseur.fetchall()#après ajout dans ED, on récupère l'Identifiant pour l'ajout dans JV

Pour la fonction ajouter(), nous avons créé 5 entrées correspondant aux valeurs du jeu à ajouter à la relation, et un bouton pour confirmer l’ajout.

Tant que le bouton de confirmation n’est pas cliqué, l’enregistrement n’existe pas dans la base de données.

Lorsque l’utilisateur confirme son ajout, une fonction ajouter_bis() est lancée, exécutant simplement :

valeur_1 = entree_1.get()
valeur_2 = entree_2.get()
valeur_3 = entree_3.get()
valeur_4 = entree_4.get()
#Id_Editeur récupéré par comme précédemment
data = (valeur_1, valeur_2, valeur_3, valeur_4, Id_Editeur)
curseur.execute("INSERT INTO JV (Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?,?,?,?,?)",data)

Ensuite, nous avons créé la fonction modifier(). Nous avons créé deux boutons permettant de modifier soit la relation Jeux Vidéo, soit la relation Editeurs.

Pour modifier la relation Editeur, on peut choisir l’éditeur à modifier soit par son Id_Editeur, soit par son nom actuel grâce à un bouton déroulant. Nous avons donc créé ce bouton déroulant et deux entrées correspondant d’abord au nouveau nom de l’éditeur, et ensuite à l’editeur à modifier. La commande SQL qui s’exécute est la suivante :

if bouton_attribut.get() == "Editeur"	
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Editeur = ?",(entree_1.get(),entree_2.get()))
else :
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Id_Editeur = ?",(entree_1.get(),entree_2.get())  

Si l’éditeur indiqué par son Id_Editeur ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que l’éditeur n’existe pas.

La fonction pour modifier un enregistrement de la relation principale est, dans les grandes lignes la même, mais l’utilisateur doit choisir l’attribut à modifier, avec un bouton déroulant. Ainsi :

curseur.execute("UPDATE ED SET Attribut_1 = ? WHERE Attribut_2 = ?",(entree_1.get(),entree_2.get())

« Attribut_1 » et « Attribut_2 » sont des valeurs fixes, testés par les boutons déroulants (demandant de nombreux tests « if » et « elif »)

Pour finir, il fallait pouvoir supprimer des éléments de la relation principale (la relation secondaire Editeurs ne peut que être lue ou modifiée). Deux attributs permettent d’identifier un jeu : son identifiant unique, et son nom (qui peut ne pas être unique). Pourtant nous avons choisi qu’il serait possible de supprimer un jeu grâce à son nom pour un utilisateur qui ne connaîtrait pas l’identifiant du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Par la suite, quelque soit le choix de l’utilisateur, le fonction est le même : une entrée apparaît, dans laquelle il est possible de renseigner soit le jeu, soit l’id du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est supprimé et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Nous avons donc créé les quatre fonctions pour interagir avec une base de données, soit la création, la lecture, la modification et la suppression de la base.

III. Conclusion

Ce compte-rendu touche maintenant à sa fin. N’hésitez pas à aller voir le tutoriel de Aël pour en apprendre plus sur la bibliothèque tkinter. Le fichier zip contenant tous les éléments nécessaires au bon fonctionnement du programme est disponible ci-dessous.

Projets

Agence de voyage et base de données

Une base de données sur des destinations paradisiaques tropicales. Consulter et réserver un voyage en exploitant une base de données dans python. L’interface utilisateur est gérée par tkinter

1 – Organiser les idées

Tout bon projet commence par un plan, afin de pouvoir poser et organiser l’idée de ce que l’on veut créer. Nous avons donc commencé par dessiner la base de notre projet :

2 – Création de nos tables

Une fois l’idée posée, il est temps de passer au programme !

Avant toute chose nous avons créé nos 4 tables et leurs attributs pour y ajouter après nos fichiers .csv créés à l’avance :

#création des tables
curseur.execute("CREATE TABLE IF NOT EXISTS DESTINATION (Id_Destination INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Nom TEXT, Continent TEXT, Temp_Moy INT, Note INT)")
curseur.execute("CREATE TABLE IF NOT EXISTS CLIENT(Id_Client INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Prenom TEXT, Nom TEXT, Lieu_depart TEXT)")
curseur.execute("CREATE TABLE IF NOT EXISTS VOL(Id_Vol INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Destination INT, Lieu_Depart TEXT, Date_Loc_Depart DATE, Heure_Loc_Depart TIME, Temps_Vol TIME, Prix INT, FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Lieu_Depart) REFERENCES CLIENT(Id_Client))")
curseur.execute("CREATE TABLE IF NOT EXISTS RESERVATION(Id_Reservation INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Client INT, Id_Destination INT, Id_Vol INT, Num_Siege INT,FOREIGN KEY (Id_Client) REFERENCES CLIENT(Id_Client), FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Id_Vol) REFERENCES VOL(Id_Vol))")
connection.commit()

Une fois les tables créées ont les a rempli avec les enregistrements contenus dans les fichiers .csv grâce au module panda:

#ajout des enregistrements .csv
pandas.read_csv("Client.csv").to_sql("CLIENT", connection, if_exists='replace', index=False)
pandas.read_csv("Destination.csv").to_sql("DESTINATION", connection, if_exists='replace', index=False)
pandas.read_csv("Vol.csv").to_sql("VOL", connection, if_exists='replace', index=False)
pandas.read_csv("Reservation.csv").to_sql("RESERVATION", connection, if_exists='replace', index=False)
connection.commit()

3 – La fenêtre d’accueil

A partir de là a commencé la création de la page d’accueil (dans les règles de l’art !)

Ici on définit l’aspect de notre fenêtre d’accueil, qui servira de modèle par la suite aux autres fenêtres.

#création de la fenêtre principale
window = Tk()
window.title("Agence de voyage : Partir un Jour sans Retour")
window.geometry("1080x720")
window.minsize(1080,775)
window.iconbitmap("island.ico")
window.config(background = "#B5D7F1")

Dans cette fenêtre on ajoute également 3 boutons, “destinations”, “réserver” et “supprimer son compte client” où les grandes actions s’effectuent.

4 – Fenêtre des destinations

Notre bouton destination mène donc à une fenêtre où les clients pourront découvrir les destinations proposées.

Les destinations sont d’abord triées par continent et rangées dans les boîtes correspondantes.

Ouvertes, ces boîtes affichent les destinations possibles du continent, rangées en tableau grâce à Treeview.

    tableau_europe = ttk.Treeview(boite_europe, columns=('Nom', 'Températures Moyennes', 'Note'), show = 'headings')
    tableau_europe.heading('Nom', text='Nom')
    tableau_europe.heading('Températures Moyennes', text='Températures Moyennes')
    tableau_europe.heading('Note', text='Note')
    
    for row in curseur.execute("SELECT Nom, Temp_Moy, Note FROM DESTINATION WHERE Continent = 'Europe' ORDER BY Nom"):
        tableau_europe.insert('', tk.END, values = row)
   
    tableau_europe.grid(row=0, column=0, sticky='nsew')

Nous avons ensuite utilisé Style() afin de personnaliser nos tableaux avec tous les attributs servant à les modifier comme sur l’image ci-dessous :

    #Style tableau europe
    
    tableau_europe = ttk.Style()
    #Prendre un theme
    tableau_europe.theme_use("clam")
    #configurer les couleurs du treeview
    tableau_europe.configure("Treeview",
        background = "#FEF3EA",
        foreground = "#206093",
        rowheight = 30,
        fieldbackground = "#FEF3EA"             
        )
    #Changer la couleur sélectionné par la souris
    tableau_europe.map('Treeview',
        background=[('selected', '#FFEE93')])

Ainsi qu’un bouton retour afin de fermer la boîte ouverte et de nouveau afficher les autres.

5 – Fenêtre réservation

Pour réserver un vol, il faut pouvoir choisir sa destination et s’inscrire dans la table Client.

Le plus simple a été de relier notre menu déroulant à la base de données. 

choix_destination = curseur.execute("SELECT Nom FROM DESTINATION")
    option_destination = OptionMenu(reservation, variable_destination, *choix_destination)
    option_destination.config(width=35, font=('Consolas', 12))

C’est au niveau du bouton de validation où cela s’est légèrement corsé.

Pas de soucis pour récupérer les textes entrés par l’utilisateur, ni son identifiant, qui constitueront son enregistrement dans la table Client.

        temp_client = (variable_prenom.get(), variable_nom.get())
        curseur.execute("INSERT INTO CLIENT(Prenom, Nom) VALUES (?,?)", temp_client)
        
        curseur.execute("SELECT MAX(Id_Client) FROM CLIENT")
        tempclient = curseur.fetchone()

C’est la récupération de l’identifiant de la destination qui aura posé le souci majeur puisque la donnée récupérée du menu déroulant se présentait comme un tuple sans en être un, enfin rien d’insurmontable un rien de bricolage suffit à régler le souci !

        temp_tempdestination = variable_destination.get()
        temp_bricodestination = (temp_tempdestination[2:-3],)
        curseur.execute("SELECT Id_Destination FROM DESTINATION WHERE Nom = ?", temp_bricodestination)
        temp_destination = curseur.fetchone()

On récupère également l’identifiant du vol

curseur.execute("SELECT Id_Vol FROM VOL WHERE Id_Destination = ?", temp_destination)
        temp_vol = curseur.fetchone()

Et une fois tous les attributs nécessaires à un enregistrement dans la relation réservation récupérés, on peut les y ajouter :

temp_reservation = (tempclient[0], temp_destination[0], temp_vol[0], randint(1, 600))
        curseur.execute("INSERT INTO RESERVATION(Id_Client, Id_Destination, Id_Vol, Num_Siege) VALUES (?,?,?,?)", temp_reservation)
        connection.commit()

6 – Fenêtre suppression de client

 Enfin si un client souhaite supprimer son compte nous avons la fenêtre “supprimer”.

Elle est composée d’une entrée utilisateur, où le client pourra renseigner son identifiant et d’un bouton “Valider” qui exécutera la suppression.

    def valider_suppression():
        temp_supprimer = variable_idclient.get()
        curseur.execute("DELETE FROM CLIENT WHERE Id_Client = ?", temp_supprimer)

Interface Tkinter réalisée avec l’aide du tutoriel d’Aël D.

Voici notre code compressé au format 7zip :

Découvrir

nsi42.net : Les projets web crées par nos élèves

Le site https://nsi42.net/ héberge les projets web de nos élèves. Codé par un ancien élève, ce site propose un jeu de piste à travers une interface en ligne de commande…

En classe de première

En classe de première, les élèves réalise un premier site web de quelques pages en février, et ils s’initient aux protocoles HTTP, HTTPS, FTP à travers ces projets.

C’est l’occasion de leur faire découvrir les langages HTML et CSS.

En classe de terminale

En classe de terminale, les élèves réalisent un premier site web dans lequel ils doivent intégrer des modules imposés et des modules librement choisis en octobre.

L’objectif est alors d’approfondir leurs connaissances en JAVASCRIPT.

Plus tard dans l’année, ils peuvent utiliser cet espace web pour mener à bien un projet centré sur les bases de données, et ce frotter ainsi aux langages PHP et SQL.

Découvrir le site nsi42.net 

Le site https://nsi42.net/ sera peut-être pour vous une découverte de l’ASCII Art et qui sait, peut-être que la console accepte les commandes Linux !

Découvrir

Le programme de la spécialité NSI

Le programme enseigné en spécialité NSI est défini par le Ministère de l’Éducation nationale et de la Jeunesse. Ce programme est commun à tous les lycées, il est librement consultable en ligne.

A noter :

Un élève curieux qui lirait le programme pourrait penser qu’il est complexe, notamment la lecture des éléments du programme, mais il s’agit d’un biais cognitif, lire le programme de spécialité maths avec le même regard naïf permet de faire le même constat, et il est tout aussi erroné.

Préambule

du programme de classe de 1ère NSI

L’enseignement de spécialité de numérique et sciences informatiques du cycle terminal de la voie générale vise l’appropriation des fondements de l’informatique pour préparer les élèves à une poursuite d’études en les formant à la pratique d’une démarche scientifique et en développant leur appétence pour des activités de recherche.

L’objectif de cet enseignement général est l’appropriation des concepts et des méthodes qui fondent l’informatique, dans ses dimensions scientifiques et techniques. Il s’appuie sur l’universalité de quatre concepts fondamentaux et la variété de leurs interactions :

  • les données, qui représentent sous une forme numérique unifiée des informations très diverses : textes, images, sons, mesures physiques, sommes d’argent, etc.
  • les algorithmes, qui spécifient de façon abstraite et précise des traitements à effectuer sur les données à partir d’opérations élémentaires ;
  • les langages, qui permettent de traduire les algorithmes abstraits en programmes textuels ou graphiques de façon à ce qu’ils soient exécutables par les machines ;
  • les machines, et leurs systèmes d’exploitation, qui permettent d’exécuter des programmes en enchaînant un grand nombre d’instructions simples, assurent la persistance des données par leur stockage et gèrent les communications. Y sont inclus les objets connectés et les réseaux

À ces concepts s’ajoute un élément transversal : les interfaces qui permettent la communication, la collecte des données et la commande des systèmes

Cet enseignement prolonge les enseignements d’informatique dispensés à l’école primaire, au collège en mathématiques et en technologie et, en seconde, l’enseignement commun de sciences numériques et technologie. Il s’appuie aussi sur l’algorithmique pratiquée en mathématiques en seconde.

Il permet de développer des compétences :

  • analyser et modéliser un problème en termes de flux et de traitement d’informations ;
    décomposer un problème en sous-problèmes, reconnaître des situations déjà analysées et réutiliser des solutions ;
  • concevoir des solutions algorithmiques ;
  • traduire un algorithme dans un langage de programmation, en spécifier les interfaces et les interactions, comprendre et réutiliser des codes sources existants, développer des processus de mise au point et de validation de programmes ;
  • mobiliser les concepts et les technologies utiles pour assurer les fonctions d’acquisition, de mémorisation, de traitement et de diffusion des informations ;
  • développer des capacités d’abstraction et de généralisation

Cet enseignement a vocation à multiplier les occasions de mise en activité des élèves, sous des formes variées qui permettent de développer des compétences transversales :

  • faire preuve d’autonomie, d’initiative et de créativité ;
  • présenter un problème ou sa solution, développer une argumentation dans le cadre d’un débat ;
  • coopérer au sein d’une équipe dans le cadre d’un projet ;
  • rechercher de l’information, partager des ressources ;
  • faire un usage responsable et critique de l’informatique.

La progression peut suivre un rythme annuel construit autour de périodes spécifiques favorisant une alternance entre divers types d’activités.

Cet enseignement contribue à l’acquisition des compétences numériques qui font l’objet d’une certification en fin de cycle terminal.

Comme tous les enseignements, cette spécialité contribue au développement des compétences orales à travers notamment la pratique de l’argumentation. Celle-ci conduit à préciser sa pensée et à expliciter son raisonnement de manière à convaincre. Elle permet à chacun de faire évoluer sa pensée, jusqu’à la remettre en cause si nécessaire, pour accéder progressivement à la vérité par la preuve. Si ces considérations sont valables pour tous les élèves, elles prennent un relief particulier pour ceux qui choisiront de poursuivre cet enseignement de spécialité en terminale et qui ont à préparer l’épreuve orale terminale du baccalauréat. Il convient que les travaux proposés aux élèves y contribuent dès la classe de première.

Démarche de projet

Un enseignement d’informatique ne saurait se réduire à une présentation de concepts ou de méthodes sans permettre aux élèves de se les approprier en développant des projets.

Un quart au moins de l’horaire total de la spécialité est réservé à la conception et à l’élaboration de projets conduits par les élèves.

Les projets réalisés par les élèves, sous la conduite du professeur, constituent un apprentissage fondamental tant pour la compréhension de l’informatique que pour l’acquisition de compétences. En classe de première comme en classe terminale, ils peuvent porter sur des problématiques issues d’autres disciplines et ont essentiellement pour but
d’imaginer des solutions répondant à l’expression d’un besoin ; dans la mesure du possible, il convient de laisser le choix du thème du projet aux élèves eux-mêmes. Il peut s’agir d’un approfondissement théorique des concepts étudiés en commun, d’une application à d’autres disciplines telle qu’une simulation d’expérience, d’un travail sur des données
socioéconomiques, du développement d’un logiciel de lexicographie, d’un projet autour d’un objet connecté ou d’un robot, de la conception d’une bibliothèque implémentant une structure de données complexe, d’un problème de traitement d’image ou de son, d’une application mobile, par exemple de réalité virtuelle ou augmentée, du développement d’un site Web associé à l’utilisation d’une base de données, de la réalisation d’un interprète d’un
mini-langage, d’un programme de jeu de stratégie, etc.

La gestion d’un projet inclut des points d’étape pour faire un bilan avec le professeur, valider des éléments, contrôler l’avancement du projet ou adapter ses objectifs, voire le redéfinir partiellement, afin de maintenir la motivation des élèves.

Les professeurs veillent à ce que les projets restent d’une ambition raisonnable afin de leur permettre d’aboutir.

Modalités de mise en œuvre

Les activités pratiques et la réalisation de projets supposent que chaque élève ait un accès individuel à un équipement relié à Internet.

Un langage de programmation est nécessaire pour l’écriture des programmes : un langage simple d’usage, interprété, concis, libre et gratuit, multiplateforme, largement répandu, riche de bibliothèques adaptées et bénéficiant d’une vaste communauté d’auteurs dans le monde éducatif est à privilégier. Au moment de la conception de ce programme, le langage choisi est Python version 3 (ou supérieure). L’expertise dans tel ou tel langage de programmation n’est cependant pas un objectif de formation

Éléments de programme

Si vous souhaitez lire l’intégralité du programme officiel, vous pouvez le télécharger sur le site eduscol.education.fr
Une copie au format .pdf est disponible ci-dessous dans l’éventualité ou le lien précédent est brisé.

Découvrir

La programmation en spé NSI

En spécialité NSI, on étudie principalement le langage de programmation Python, dans lequel on écrit les algorithmes traité en classe, ainsi que le langage SQL qui permet d’interroger une base de donnée.
Les projets réalisés par les élèves leurs permettent de découvrir et de s’amuser avec du HTML, du CSS, du JavaScript.
La découverte des systèmes d’exploitations est l’occasion de s’initier au BASH.

A noter :

L’expertise dans tel ou tel langage de programmation n’est cependant pas un objectif de formation, en spécialité NSI on découvre des langages, chacun est libre d’approfondir ces connaissances lors de la réalisations des projets.

Les différents langages découverts ou approfondis

Python

Un langage de programmation est nécessaire pour l’écriture des programmes.
Les lycées français ont choisis le langage Python, qui est à la fois simple d’usage, interprété, concis, libre et gratuit, multiplateforme et largement répandu.

# Python 3
print("Hello NSI !")

HTML

Aujourd’hui, de plus en plus de nos actions se passent par le Web, qui est aux centres de nos vies. Il faut donc pour cela expliquer aux spécialités NSI, son organisation, ses complexités, et à commencer par l’HTML. Ce n’est pas vraiment un langage, car il fait le lien entre plusieurs langages, mais il est l’élément central pour le fonctionnement des pages Web comme celle que vous êtes en train de regarder.

<strike class="physique">La Physique c'est bien</strike>
<h1 id="nsi">La NSI c'est super</h1>

CSS

Nous retrouvons donc le CSS qui va de pair avec le HTML, si ce dernier est l’élément central, sans le CSS, le Web ressemblerait à la première page Web. C’est donc ce qui permet de donner un petit peu de style au Web, pour le rendre plus attrayant.

#nsi {
  color : purple;
}
.physique {
  background-color: black;
}

JavaScript

Le JavaScript est aussi un des éléments clés du fonctionnement du Web que nous aborderons en spécialité NSI. Il permet de faire fonctionner certains éléments de façon dynamique, comme un menu, mais aussi certaines interactions avec l’utilisateur comme sur notre portail des élèves et a aussi bien d’autres avantages, que vous pourrez découvrir dans le cadre des projets en spé NSI.

//let nsi;
//nsi = "En classe de première, je choisis la spécialité NSI";
window.alert("En classe de première, je choisis la spécialité NSI");

SQL

Le SQL permet de traiter simplement d’immenses bases de données, de les classifier, les regrouper, etc… Il est extrêmement puissant tout en étant d’une simplicité déconcertante, et permet à nos élèves, de mettre un pied dans le monde des bases de données immenses, ou la moindre recherche manuelle serait un parcours du combattant.

SELECT *
FROM nsi

BASH

Le BASH, c’est magique, peu de personnes vous le diront car elles n’ont pas l’occasion de l’utiliser au quotidien et de découvrir sa praticité. Mais c’est quoi au fait ? C’est tout simplement la langue du terminal, vous savez cette fenêtre noire et peu rassurante qui s’ouvre de temps en temps. Nos élèves découvrent donc cette interface au travers du monde incroyable de Linux/GNU, à travers un petit jeu interactif en ligne : Terminus

mkdir nsi
cd nsi
sudo apt-get install reussite
./reussiteNSI.elf

Les autres fondamentaux en spé NSI

De la même manière qu’un élève étudiant la spécialité Physique au lycée ne sera pas obligatoirement en charge de piloter une centrale nucléaire, les élèves étudiant la spécialité NSI ne seront pas tous développeurs informatique, et ne feront peut-être même pas un métier lié à l’informatique. Cette spécialité est ouverte à tous, filles comme garçons, intéressés de découvrir le fonctionnement des outils qui nous entourent de plus en plus, et désireux d’acquérir des compétences pour le futur.

La spécialité NSI est l’occasion de découvrir une nouvelle science, appelée informatique.
Les concepts fondamentaux étudiés en spécialité NSI vont bien au delà de la simple conception de programme, ils permettent de rentrer et de découvrir ce monde fabuleux que sont les sciences informatiques.

Découvrir

Présentation de la spécialité NSI

L’enseignement de spécialité de numérique et sciences informatiques (NSI) du cycle terminal de la voie générale vise l’appropriation des fondements de l’informatique pour préparer les élèves à une poursuite d’études dans l’enseignement supérieur, en les formant à la pratique d’une démarche scientifique et en développant leur appétence pour des activités de recherche.

Objectifs de la spécialité NSI

L’objectif de cet enseignement est l’appropriation des concepts et des méthodes qui fondent l’informatique, dans ses dimensions scientifiques et techniques.

Cet enseignement s’appuie sur l’universalité de quatre concepts fondamentaux et la variété de leurs interactions :

  • les données ,
  • les algorithmes,
  • les langages,
  • les machines.

Profils recherchés

La spécialité NSI est proposée au lycée Louis Pasteur aux élèves ayant choisi de préparer un baccalauréat général.

Cet enseignement de 4 heures par semaine en classe de première a lieu en salle informatique exclusivement, il prépare les élèves aux études scientifiques mais il peut aussi être suivi par des élèves ayant un profil plus littéraire.

Organisation des enseignements au lycée Louis Pasteur

L’enseignement de la spécialité NSI au lycée Louis Pasteur place l’élève au cœur du dispositif.

Chaque élève construit son propre cours, en partir de documents numériques préparés par les enseignants du lycée, chacun peut avancer à son rythme.

Environ 1/4 du temps scolaire est consacré à la réalisation du mini-projets ou de tutoriels.

Les projets et les tutoriels réalisés par nos étudiants donnent lieu à :

  • la rédaction d’un compte rendu, publié sur ce site
  • une présentation orale devant la classe, indispensable entrainement au grand oral du baccalauréat.