From 4627dec805cd9a93bfa668ab6259edb89524f1a2 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 21:46:40 +0100 Subject: [PATCH 01/18] Add the documentation PDF --- .gitignore | 2 +- documentation/documentation.pdf | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 documentation/documentation.pdf diff --git a/.gitignore b/.gitignore index 35a1122..5a51eaa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ documentation/* *.sqlite3 *.csv -#!documentation/documentation.pdf +!documentation/documentation.pdf !documentation/documentation.tex diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf new file mode 100644 index 0000000..372e36f --- /dev/null +++ b/documentation/documentation.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fffdccb227e261f89d7400d314c2b63e438242a58d909f4fd40d117d79bc658a +size 488235 From 315bc8635ea764164b3a0385aa356306aad16e49 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 22:13:40 +0100 Subject: [PATCH 02/18] reverse manager and disconnect button in cashier interface --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 0e7b6bc..2534403 100644 --- a/main.py +++ b/main.py @@ -579,11 +579,11 @@ class GesMag: Stats().exporteCSV(chemin.name, id) Button(self.f, text="Exporter les statistiques", font=self.font, command=__exportation).grid(column=0, row=2, sticky='e', padx=ecart) + Button(self.f, text="Se déconnecter", font=self.font, command=self._interfaceConnexion).grid(column=0, row=2, sticky='w', padx=ecart) + # -> Boutton pour passer en mode manager si la personne est un manager if caissier["metier"] == 0: - Button(self.f, text="Passer en mode Manager", font=self.font, command=lambda: self._interfaceManager(id)).grid(column=0, row=2, sticky='w', padx=ecart) - - Button(self.f, text="Se déconnecter", font=self.font, command=self._interfaceConnexion).grid(column=0, row=2) + Button(self.f, text="Passer en mode Manager", font=self.font, command=lambda: self._interfaceManager(id)).grid(column=0, row=2, sticky='w', padx=220) def _interfaceManager(self, id: int): """Affiche l'interface du manager.""" From 33f6b4d47bf6a4ccca5b2bcb3497d4d6bd0db887 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 22:15:23 +0100 Subject: [PATCH 03/18] update link for the documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef34626..979ad34 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,6 @@ Le compte par défaut créer lors de la création de la base de donnée est : | Numéro d'étudiant | 20010664 | | Mail | anri.kennel@etud.univ-paris8.fr | -Pour plus d'informations, [veuillez-vous référez à la documentation](documentation/documentation.pdf) +Pour plus d'informations, [veuillez-vous référez à la documentation](https://git.kennel.ml/Anri/GesMag/media/branch/master/documentation/documentation.pdf) Pour cloner le projet, vous devez avoir d'installer [Git LFS](https://git-lfs.github.com/) sur votre machine (-> pour les images). From 2edd7511bd013cb795ebef559c56020bf08269d4 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 22:25:12 +0100 Subject: [PATCH 04/18] addition of missing types and comments of functions --- db.py | 2 +- main.py | 50 +++++++++++++++++++++++++++----------------------- stats.py | 8 ++++---- users.py | 2 +- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/db.py b/db.py index b1476c0..0c5dcd0 100644 --- a/db.py +++ b/db.py @@ -25,7 +25,7 @@ class BaseDeDonnees: else: # si le fichier existe return True - def requete(self, requete: str, valeurs = None): + def requete(self, requete: str, valeurs = None) -> tuple: """Envois une requête vers la base de données.""" try: curseur = self.connexion.cursor() diff --git a/main.py b/main.py index 2534403..ea34e23 100644 --- a/main.py +++ b/main.py @@ -112,7 +112,7 @@ class GesMag: return False return True - def connexion(self, utilisateur: str, motDePasse: str): + def connexion(self, utilisateur: str, motDePasse: str) -> None: """Gère la connexion aux différentes interfaces de l'application.""" """ Vérification nom d'utilisateur / mot de passe correctement entré @@ -139,7 +139,7 @@ class GesMag: else: showerror("Erreur", "Utilisateur ou mot de passe incorrect.") - def dimensionsFenetre(self, fenetre, nouveauX: int, nouveauY: int): + def dimensionsFenetre(self, fenetre, nouveauX: int, nouveauY: int) -> None: """Permet de changer les dimensions de la fenêtre parent et la place au centre de l'écran.""" largeur = fenetre.winfo_screenwidth() hauteur = fenetre.winfo_screenheight() @@ -149,7 +149,7 @@ class GesMag: fenetre.geometry(f"{nouveauX}x{nouveauY}+{x}+{y}") - def _interfaceConnexion(self): + def _interfaceConnexion(self) -> None: """Affiche l'interface de connexion.""" # Paramètres de la fenêtre self.dimensionsFenetre(self.parent, 400, 600) @@ -175,7 +175,7 @@ class GesMag: motDpasse.grid(column=1, row=4, columnspan=2, padx=ecart) motDpasse.bind("", tentativeDeConnexion) - def __afficherMDP(self): + def __afficherMDP(self) -> None: """Permet de gérer l'affichage du mot de passe dans le champs sur la page de connexion.""" if self.mdpVisible == False: # si mot de passe caché, alors on l'affiche self.mdpVisible = True @@ -197,7 +197,7 @@ class GesMag: Button(self.f, text="Quitter", font=self.font, command=quit).grid(column=0, row=6, columnspan=4, pady=20) - def _interfaceCaissier(self, id: int): + def _interfaceCaissier(self, id: int) -> None: """Affiche l'interface du caissier.""" caissier = Utilisateurs().recuperationUtilisateur(id=id) self.parent.title(f"Caissier {caissier['nom']} {caissier['prenom']} – {self.nomApp}") @@ -234,7 +234,7 @@ class GesMag: boucheriePoissonnerieVerif = IntVar(stock) entretienVerif = IntVar(stock) - def __affichageTableau(page: int = 1): + def __affichageTableau(page: int = 1) -> None: """Fonction qui va actualiser le tableau avec une page donnée (par défaut affiche la première page).""" # On supprime et refais la frame qui va stocker notre tableau self.tableau.destroy() @@ -254,7 +254,7 @@ class GesMag: stockListe = Stock().listeStocks() # stock récupéré de la base de données - def ___miseAJourPanier(element: dict, action: bool): + def ___miseAJourPanier(element: dict, action: bool) -> None: """ Permet d'ajouter ou de retirer des éléments au panier -> Action @@ -387,7 +387,7 @@ class GesMag: Label(ticket, text=f"Date de vente : {date.today().strftime('%Y/%m/%d')}").grid(column=0, row=0, pady=ecart) - def __affichagePanier(): + def __affichagePanier() -> None: """Affiche le panier actuel dans le ticket de caisse.""" self.panierAffichage.destroy() self.panierAffichage = Frame(ticket) @@ -420,7 +420,7 @@ class GesMag: __affichagePanier() - def __validationTicketDeCaisse(): + def __validationTicketDeCaisse() -> None: """Lance plusieurs méthodes pour valider le ticket de caisse.""" # Met à jour la valeur dans le fichier `CSV` (statistiques) Stats().miseAJourStatsUtilisateur(id, sum([element[0]["prix"] * element[1] for element in self.panier])) @@ -443,7 +443,7 @@ class GesMag: validationTicketDeCaisseBouton.grid(column=0, pady=ecart) # -> Partie ajout élément au stock - def __ajouterElementStock(): + def __ajouterElementStock() -> None: """Ouvre une fenêtre qui permet d'ajouter un nouvel élément à la base de donnée.""" """ L'enfant (`TopLevel`) dépend de la `Frame` et non du parent (`Tk`) @@ -452,7 +452,7 @@ class GesMag: enfant = Toplevel(self.f) enfant.title(f"Ajouter un élément au stock – {self.nomApp}") - def ___verification(): + def ___verification() -> None: """Vérifie si les champs renseignées sont valides.""" """ La variable `ok` sert à savoir si la vérification est passée @@ -473,6 +473,10 @@ class GesMag: # Pas de coloration orange si le type est mauvais parce que on ne peut pas changé la couleur de fond d'une ComboBox # vérification pour le nom def ___nomValide(nom: str) -> bool: + """ + Vérifie si un nom est valide pour le stock. + (non vide et pas déjà présent dans la base de donnée) + """ if len(nom) <= 0: return False if Stock().stockExistant(nom) == True: @@ -528,7 +532,7 @@ class GesMag: Label(enfant, text="Image :").grid(column=0, row=0, sticky='e') image = Entry(enfant) image.grid(column=1, row=0, sticky='w') - def ___selectionImage(): + def ___selectionImage() -> None: """Fonction qui permet de choisir une image dans l'arborescence de fichiers de l'utilisateur.""" try: chemin = askopenfile(title="Choisir une image", filetypes=[("Image GIF", ".gif")]) @@ -556,7 +560,7 @@ class GesMag: prix = Entry(enfant) prix.grid(column=1, row=4, sticky='w') - def ___viderChamps(): + def ___viderChamps() -> None: """Vide tout les champs de leur contenu""" # On récupère toutes les `Entry` de la fenêtre et on change leur contenu for champ in [widget for typeElement, widget in enfant.children.items() if "entry" in typeElement]: @@ -571,7 +575,7 @@ class GesMag: Button(self.f, text="Ajouter un élément\nau stock", font=self.font, command=__ajouterElementStock).grid(column=1, row=2) # -> Partie export des statistiques - def __exportation(): + def __exportation() -> None: """Exporte dans un fichier choisie par l'utilisateur ses statistiques de la journée.""" chemin = asksaveasfile(title=f"Exportation des statistiques de {caissier['nom']} {caissier['prenom']}", filetypes=[("Fichier CSV", ".csv")]) if chemin == None: # si rien n'a été spécifie on arrête l'exportation @@ -585,7 +589,7 @@ class GesMag: if caissier["metier"] == 0: Button(self.f, text="Passer en mode Manager", font=self.font, command=lambda: self._interfaceManager(id)).grid(column=0, row=2, sticky='w', padx=220) - def _interfaceManager(self, id: int): + def _interfaceManager(self, id: int) -> None: """Affiche l'interface du manager.""" manager = Utilisateurs().recuperationUtilisateur(id=id) # Dans le cas où un utilisateur réussi à trouvé cette interface alors qu'il n'a pas le droit, il sera bloqué @@ -607,7 +611,7 @@ class GesMag: Label(self.f).grid(row = 1, pady=10) # séparateur - def __ajouterUtilisateur(metier: int): + def __ajouterUtilisateur(metier: int) -> None: """Permet de créer un nouvel utilisateur, manager (`metier = 0`) et caissier (`metier = 1`).""" """ L'enfant (`TopLevel`) dépend de la `Frame` et non du parent (`Tk`) @@ -616,7 +620,7 @@ class GesMag: enfant = Toplevel(self.f) enfant.title(f"Ajouter un {'manager' if metier == 0 else 'caissier'} – {self.nomApp}") - def ___verification(): + def ___verification() -> None: """Vérifie si les champs renseignées sont valides.""" """ Les valeurs `Entry` qui ne sont pas passés seront dans @@ -711,7 +715,7 @@ class GesMag: postal = Entry(enfant) postal.grid(column=1, row=6, sticky='w') - def ___viderChamps(): + def ___viderChamps() -> None: """Vide tout les champs de leur contenu""" # On récupère toutes les `Entry` de la fenêtre et on change leur contenu for champ in [widget for typeElement, widget in enfant.children.items() if "entry" in typeElement]: @@ -723,7 +727,7 @@ class GesMag: Button(enfant, text="Vider les champs", command=___viderChamps).grid(column=0, row=8, columnspan=3) Button(enfant, text="Quitter", command=enfant.destroy).grid(column=0, row=8, columnspan=3, sticky='e') - def __retirerUtilisateur(metier: int): + def __retirerUtilisateur(metier: int) -> None: """Permet de supprimer un utilisateur existant, manager (`metier = 0`) et caissier (`metier = 1`).""" enfant = Toplevel(self.f) # cf. l'explication dans `__ajouterUtilisateur` enfant.title(f"Retirer un {'manager' if metier == 0 else 'caissier'} – {self.nomApp}") @@ -741,7 +745,7 @@ class GesMag: listeUtilisateurs_retirer.grid(column=0, row=1) # On affiche l'utilisateur quand on double-clique dessus - def ___suppressionUtilisateur(): + def ___suppressionUtilisateur() -> None: """Supprime l'utilisateur actuellement sélectionné dans la liste""" element = listeUtilisateurs_retirer.curselection() if len(element) == 0: # si aucun élément n'est selectionné @@ -759,7 +763,7 @@ class GesMag: Button(enfant, text="Supprimer", command=___suppressionUtilisateur).grid(column=0, row=8, columnspan=3, sticky='w') Button(enfant, text="Quitter", command=enfant.destroy).grid(column=0, row=8, columnspan=3, sticky='e') - def __afficherInformationsUtilisateur(_): + def __afficherInformationsUtilisateur(_) -> None: """Permet d'afficher les informations d'un utilisateur""" element = listeUtilisateurs.curselection() if len(element) == 0: # si aucun élément n'est selectionné @@ -789,7 +793,7 @@ class GesMag: frameSuivi = LabelFrame(enfant, text="Histogramme des ventes", font=self.font) frameSuivi.grid(column=1, row=0, sticky="ns", padx=5) - def ___actualisationCanvas(): + def ___actualisationCanvas() -> None: """Affiche l'histogramme des vente d'un utilisateur dans un canvas.""" donnees = Stats().recuperationDonneesCSV(utilisateur['id']) if len(donnees) <= 0: @@ -845,7 +849,7 @@ class GesMag: caissierVerif.set(1) # par défaut on affiche que les caissiers - def __ajouterUtilisateursListe(liste: Listbox, force: int = None): + def __ajouterUtilisateursListe(liste: Listbox, force: int = None) -> None: """ Ajoute des utilisateurs à la liste du Manager. -> metier = 0 : manager uniquement diff --git a/stats.py b/stats.py index b011cae..5975e35 100644 --- a/stats.py +++ b/stats.py @@ -18,7 +18,7 @@ class Stats(): dateAujourdHui = dateAujourdHui + timedelta(days=1) return datesPossibles - def creationCSV(self, force: bool = False): + def creationCSV(self, force: bool = False) -> None: """ Créer le fichier `CSV` qui stockera les statistiques pour tous les utilisateurs. @@ -30,7 +30,7 @@ class Stats(): fichier = csv.writer(f) fichier.writerow(["id", "pseudo"] + self.datesDisponibles()) - def miseAJourStatsUtilisateur(self, utilisateurID: int, prix: float): + def miseAJourStatsUtilisateur(self, utilisateurID: int, prix: float) -> None: """ Récupère le prix d'une transaction et l'ajoute au total d'un utilisateur. @@ -84,7 +84,7 @@ class Stats(): ecriture.writerows(fichier) - def exporteCSV(self, chemin: str, utilisateurID: int): + def exporteCSV(self, chemin: str, utilisateurID: int) -> None: """ Exporte les statistiques d'un utilisateur dans un fichier `CSV`. - N'exporte que les statistiques du jour. @@ -109,7 +109,7 @@ class Stats(): return utilisateur # renvoie des infos de l'utilisateur return {} # ne retourne rien si l'utilisateur n'était pas présent dans le fichier - def miseAJourDatesCSV(self): + def miseAJourDatesCSV(self) -> None: """ Mets-à-jour les dates trop anciennes du fichier globales `CSV`. diff --git a/users.py b/users.py index 16b625e..77aa2a9 100644 --- a/users.py +++ b/users.py @@ -55,7 +55,7 @@ class Utilisateurs(BaseDeDonnees): """ self.requete(requete, pseudo) - def verificationIdentifiants(self, pseudo: str, motDePasse: str): + def verificationIdentifiants(self, pseudo: str, motDePasse: str) -> tuple: """ Retourne l'ID de l'utilisateur si trouvé dans la base de donnée ainsi que son métier (`tuple`), sinon renvoie `(0,)`. From b0e3737f649b511f3fb6e8d66234b1117759e1d0 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 22:28:46 +0100 Subject: [PATCH 05/18] add spaces between the bars of the histogram --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index ea34e23..5debebe 100644 --- a/main.py +++ b/main.py @@ -829,7 +829,7 @@ class GesMag: histogramme.create_rectangle(ecart, 180, ecart + 15, hauteur - 20 if hauteur < 180 else 178, fill=couleurs.pop()) # Affichage du montant histogramme.create_text(ecart, 190, anchor='w', text=donnees[date], font=("Arial", 8)) - ecart += 30 + ecart += 33 histogramme = Canvas(frameSuivi, width=270, height=200) histogramme.grid() From c6e71900a05016abe42343f75b9de0da47a13cf6 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Tue, 30 Nov 2021 22:30:17 +0100 Subject: [PATCH 06/18] update PDF with latest code changes --- documentation/documentation.pdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index 372e36f..29ab914 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fffdccb227e261f89d7400d314c2b63e438242a58d909f4fd40d117d79bc658a -size 488235 +oid sha256:430762c3834117c2c3d837beab98e89d1684be16b04d7eba29486c7082f4b7fb +size 489463 From 6f665d8b7cfafe70e8eba8fd22ab1533e8746ba0 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 14:59:40 +0100 Subject: [PATCH 07/18] =?UTF-8?q?Reduce=20size=20of=20=E2=82=AC=20in=20his?= =?UTF-8?q?togram?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 5debebe..abf3699 100644 --- a/main.py +++ b/main.py @@ -828,7 +828,7 @@ class GesMag: # On fait `- 20` au résultat pour allonger la barre, aussi on met une barre de `2` pixel quand valeur petite histogramme.create_rectangle(ecart, 180, ecart + 15, hauteur - 20 if hauteur < 180 else 178, fill=couleurs.pop()) # Affichage du montant - histogramme.create_text(ecart, 190, anchor='w', text=donnees[date], font=("Arial", 8)) + histogramme.create_text(ecart, 190, anchor='w', text=donnees[date], font=("Arial", 7)) ecart += 33 histogramme = Canvas(frameSuivi, width=270, height=200) From f10864504720d1ebbccac13213a1e0a663b56a5e Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 15:02:22 +0100 Subject: [PATCH 08/18] Fix potential bug when showing histogram --- main.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/main.py b/main.py index abf3699..d9c1e52 100644 --- a/main.py +++ b/main.py @@ -814,6 +814,13 @@ class GesMag: "chocolate1", "goldenrod2" ] + + # On remplace tous les prix `None` par `0.` (au cas où il y est des valeurs vide dans le `CSV`) + for date, prix in donnees.items(): + if prix == None: + donnees[date] = 0. + + # On récupère la plus grosse vente maxVente = 0 # par défaut la meilleur vente est de 0 for prix in donnees.values(): prix = float(prix) From 6ec78fabb07a5c6dff095dd9ba5a9e1e1bd0f849 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 15:04:33 +0100 Subject: [PATCH 09/18] Prevents to resize children windows --- main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.py b/main.py index d9c1e52..a304c60 100644 --- a/main.py +++ b/main.py @@ -450,6 +450,7 @@ class GesMag: pour éviter de resté ouverte meme lorsque le caissier se déconnecte. """ enfant = Toplevel(self.f) + enfant.resizable(False, False) enfant.title(f"Ajouter un élément au stock – {self.nomApp}") def ___verification() -> None: @@ -618,6 +619,7 @@ class GesMag: pour éviter de resté ouverte meme lorsque le manager se déconnecte. """ enfant = Toplevel(self.f) + enfant.resizable(False, False) enfant.title(f"Ajouter un {'manager' if metier == 0 else 'caissier'} – {self.nomApp}") def ___verification() -> None: @@ -730,6 +732,7 @@ class GesMag: def __retirerUtilisateur(metier: int) -> None: """Permet de supprimer un utilisateur existant, manager (`metier = 0`) et caissier (`metier = 1`).""" enfant = Toplevel(self.f) # cf. l'explication dans `__ajouterUtilisateur` + enfant.resizable(False, False) enfant.title(f"Retirer un {'manager' if metier == 0 else 'caissier'} – {self.nomApp}") # Liste des utilisateurs @@ -776,6 +779,7 @@ class GesMag: """ utilisateur = Utilisateurs().recuperationUtilisateur(pseudo=listeUtilisateurs.get(element[0]).split('(')[0][:-1]) enfant = Toplevel(self.f) # cf. l'explication dans `__ajouterUtilisateur` + enfant.resizable(False, False) enfant.title(f"{utilisateur['nom']} {utilisateur['prenom']} – {self.nomApp}") # Informations sur l'utilisateur From 9cf7b46f8fcaa0eaa5f518d114baf32c835f2f24 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 15:14:23 +0100 Subject: [PATCH 10/18] Add appname to all windows --- main.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/main.py b/main.py index a304c60..c3dffbd 100644 --- a/main.py +++ b/main.py @@ -120,11 +120,11 @@ class GesMag: """ pseudoOk = self.utilisateurCorrect(utilisateur) if not pseudoOk[0]: - showerror("Erreur", pseudoOk[1]) + showerror(f"Erreur – {self.nomApp}", pseudoOk[1]) return mdpOk = self.motDePasseCorrect(motDePasse) if not mdpOk[0]: - showerror("Erreur", mdpOk[1]) + showerror(f"Erreur – {self.nomApp}", mdpOk[1]) return # Redirection vers la bonne interface @@ -135,9 +135,9 @@ class GesMag: elif utilisateurBaseDeDonnee[1] == 1: # si le métier est "Caissier" self._interfaceCaissier(utilisateurBaseDeDonnee[0]) else: - showerror("Erreur", "Une erreur est survenue : métier inconnue.") + showerror(f"Erreur – {self.nomApp}", "Une erreur est survenue : métier inconnue.") else: - showerror("Erreur", "Utilisateur ou mot de passe incorrect.") + showerror(f"Erreur – {self.nomApp}", "Utilisateur ou mot de passe incorrect.") def dimensionsFenetre(self, fenetre, nouveauX: int, nouveauY: int) -> None: """Permet de changer les dimensions de la fenêtre parent et la place au centre de l'écran.""" @@ -272,10 +272,10 @@ class GesMag: # On vérifie que on peut encore l'ajouter/retirer if nombreDeFoisPresentDansLePanier == 0 and not action: # pop-up seulement si on veut retirer un élément pas présent - showerror("Erreur", "Impossible de retirer cet élément au panier.\nNon présent dans le panier.") + showerror(f"Erreur – {self.nomApp}", "Impossible de retirer cet élément au panier.\nNon présent dans le panier.") return if nombreDeFoisPresentDansLePanier >= element["quantite"] and action: # pop-up seulement si on veut en rajouter - showerror("Erreur", "Impossible de rajouter cet élément au panier.\nLimite excédée.") + showerror(f"Erreur – {self.nomApp}", "Impossible de rajouter cet élément au panier.\nLimite excédée.") return if index != None: # on retire l'ancienne valeur du panier si déjà présente dans le panier @@ -426,7 +426,7 @@ class GesMag: Stats().miseAJourStatsUtilisateur(id, sum([element[0]["prix"] * element[1] for element in self.panier])) # Informe l'utilisateur que tout est validé - showinfo("Validation", "Ticket de caisse validé !") + showinfo(f"Validation – {self.nomApp}", "Ticket de caisse validé !") # Retire les éléments renseigné dans le panier du stock for element in self.panier: @@ -536,7 +536,7 @@ class GesMag: def ___selectionImage() -> None: """Fonction qui permet de choisir une image dans l'arborescence de fichiers de l'utilisateur.""" try: - chemin = askopenfile(title="Choisir une image", filetypes=[("Image GIF", ".gif")]) + chemin = askopenfile(title=f"Choisir une image – {self.nomApp}", filetypes=[("Image GIF", ".gif")]) image.delete(0, "end") image.insert(0, chemin.name) except AttributeError: # si l'utilisateur n'a pas choisit d'image @@ -578,7 +578,7 @@ class GesMag: # -> Partie export des statistiques def __exportation() -> None: """Exporte dans un fichier choisie par l'utilisateur ses statistiques de la journée.""" - chemin = asksaveasfile(title=f"Exportation des statistiques de {caissier['nom']} {caissier['prenom']}", filetypes=[("Fichier CSV", ".csv")]) + chemin = asksaveasfile(title=f"Exportation des statistiques de {caissier['nom']} {caissier['prenom']} – {self.nomApp}", filetypes=[("Fichier CSV", ".csv")]) if chemin == None: # si rien n'a été spécifie on arrête l'exportation return Stats().exporteCSV(chemin.name, id) @@ -595,7 +595,7 @@ class GesMag: manager = Utilisateurs().recuperationUtilisateur(id=id) # Dans le cas où un utilisateur réussi à trouvé cette interface alors qu'il n'a pas le droit, il sera bloqué if manager["metier"] != 0: - showerror("Erreur", "Vous ne pouvez pas accéder à cette interface.") + showerror(f"Erreur – {self.nomApp}", "Vous ne pouvez pas accéder à cette interface.") return self.parent.title(f"Manager {manager['nom']} {manager['prenom']} – {self.nomApp}") self.dimensionsFenetre(self.parent, 580, 310) @@ -752,15 +752,15 @@ class GesMag: """Supprime l'utilisateur actuellement sélectionné dans la liste""" element = listeUtilisateurs_retirer.curselection() if len(element) == 0: # si aucun élément n'est selectionné - showwarning("Attention", "Aucun utilisateur n'a été selectionné.") + showwarning(f"Attention – {self.nomApp}", "Aucun utilisateur n'a été selectionné.") else: utilisateur = listeUtilisateurs_retirer.get(listeUtilisateurs_retirer.curselection()[0]).split('(')[0][:-1] - reponse = askyesno("Confirmation", f"Voulez vous supprimer {utilisateur} ?") + reponse = askyesno(f"Confirmation – {self.nomApp}", f"Voulez vous supprimer {utilisateur} ?") if reponse == True: Utilisateurs().suppressionUtilisateurs(utilisateur) __ajouterUtilisateursListe(listeUtilisateurs_retirer) # met à jour la liste dans la fenêtre de suppression __ajouterUtilisateursListe(listeUtilisateurs) # met à jour la liste dans l'interface principale - showinfo("Information", f"Utilisateur {utilisateur} supprimé.") + showinfo(f"Information – {self.nomApp}", f"Utilisateur {utilisateur} supprimé.") # Boutons Button(enfant, text="Supprimer", command=___suppressionUtilisateur).grid(column=0, row=8, columnspan=3, sticky='w') From c7a07eae4394b76234f42417fe54c3c2270ee655 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 15:40:39 +0100 Subject: [PATCH 11/18] Update PDF with latest changes --- documentation/documentation.pdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index 29ab914..e871d76 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:430762c3834117c2c3d837beab98e89d1684be16b04d7eba29486c7082f4b7fb -size 489463 +oid sha256:60626ae6ad92cb507b65090019bf1837842db935c8b313f2eaef38e35d194dc0 +size 491729 From 47fc66bcf69ea629712266fda82ffba04c307ec6 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 16:49:00 +0100 Subject: [PATCH 12/18] Add title Label in login screen --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index c3dffbd..1d5617c 100644 --- a/main.py +++ b/main.py @@ -166,6 +166,7 @@ class GesMag: ecart = 80 # écart pour avoir un affichage centré Label(self.f).grid(row=0, pady=50) # utilisé pour du padding (meilleur affichage) + Label(self.f, text="Connexion", font=(self.font[0], 30)).grid(column=1, row=0, columnspan=2)# titre Label(self.f, text="Utilisateur", font=self.font).grid(column=0, row=1, columnspan=2, padx=ecart - 20, pady=20, sticky='w') utilisateur = Entry(self.f, font=self.font, width=18) utilisateur.grid(column=1, row=2, columnspan=2, padx=ecart) From 18aadb4dd8c49c6d866c8f9d3a6c2eb3f8c4da0d Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 17:11:25 +0100 Subject: [PATCH 13/18] update PDF with latest code --- documentation/documentation.pdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index e871d76..4e984bb 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60626ae6ad92cb507b65090019bf1837842db935c8b313f2eaef38e35d194dc0 -size 491729 +oid sha256:84225f01e18ed4ac0ee88b1fc7c0fbc542c6ff8416db9d214948a483664c1e79 +size 492062 From b8a4b91b9ff509fb19e5319954526a4db3379ce8 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Wed, 1 Dec 2021 21:10:58 +0100 Subject: [PATCH 14/18] Add illustration in documentation --- .gitattributes | 1 + .gitignore | 1 + documentation/documentation.pdf | 4 +- documentation/documentation.tex | 74 ++++++++++++++++++++++-- documentation/img/add_cashier.png | 3 + documentation/img/add_stock.png | 3 + documentation/img/cashier.png | 3 + documentation/img/cashier_as_manager.png | 3 + documentation/img/login.png | 3 + documentation/img/manager.png | 3 + documentation/img/receipt.png | 3 + documentation/img/remove_cashier.png | 3 + documentation/img/user_info.png | 3 + 13 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 documentation/img/add_cashier.png create mode 100644 documentation/img/add_stock.png create mode 100644 documentation/img/cashier.png create mode 100644 documentation/img/cashier_as_manager.png create mode 100644 documentation/img/login.png create mode 100644 documentation/img/manager.png create mode 100644 documentation/img/receipt.png create mode 100644 documentation/img/remove_cashier.png create mode 100644 documentation/img/user_info.png diff --git a/.gitattributes b/.gitattributes index 0b75546..cf8f1ee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.pdf filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 5a51eaa..7c65322 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ documentation/* !documentation/documentation.pdf !documentation/documentation.tex +!documentation/img diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index 4e984bb..7cd7d7d 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84225f01e18ed4ac0ee88b1fc7c0fbc542c6ff8416db9d214948a483664c1e79 -size 492062 +oid sha256:5b15d05e6f9c710673b7ee8f66a9a355645c68d6e27185860a379023a803b721 +size 985588 diff --git a/documentation/documentation.tex b/documentation/documentation.tex index 97930cc..ac4c354 100644 --- a/documentation/documentation.tex +++ b/documentation/documentation.tex @@ -1,9 +1,9 @@ \documentclass{article} -\usepackage[french]{babel} % français \usepackage[T1]{fontenc} % encodage \usepackage[hidelinks]{hyperref} % liens cliquable dans la table des matières \usepackage{graphicx} % images +\usepackage{subfigure} % deux images côte à côte \usepackage{listingsutf8} % intégration code \usepackage{xcolor} % couleurs personnalisés \usepackage{geometry} % change les dimensions de la page @@ -98,6 +98,8 @@ escapebegin=\color{ipython_green}, } +\renewcommand{\contentsname}{Table des matières} % traduction en FR + \title{\href{https://git.kennel.ml/Anri/GesMag}{Projet final Tkinter}} \author{Anri Kennel\thanks{Numéro d'étudiant : 20010664}\, (L2-A)\\Module Programmation d’interfaces $\cdot$ Paris 8} \date{Année universitaire 2021-2022} @@ -136,6 +138,13 @@ \subsection{Cahier des charges} \begin{todolist} \item[\fait] Page de login \texttt{/1.5} + + \begin{figure}[!ht] + \centering + \includegraphics[height=0.4\textheight]{img/login.png} + \caption{Page de login} + \end{figure} + \begin{todolist} \item[\fait] Nom d'utilisateur ne contient que des lettres et des chiffres \item[\fait] Mot de passe de minimum 8 caractères dont 1 caractère spécial, une majuscule et une minuscule \textbf{$\Rightarrow$ possibilité d'afficher ou non le mot de passe en clair} @@ -143,23 +152,80 @@ \item[\fait] Un bouton pour quitter l'application \end{todolist} \item[\fait] Page de manager (définit par un nom d'utilisateur et un mot de passe) \texttt{/7.5} + + \begin{figure}[!ht] + \centering + \includegraphics[height=0.3\textheight]{img/manager.png} + \caption{Page du Manager} + \end{figure} + \begin{todolist} \item[\faitDifferemment] Peut ajouter et supprimer un caissier \textbf{$\Rightarrow$ lisiblité accru pour les champs mal renseignés, l'ID n'est pas à renseigné car assigné automatiquent par la base de donnée} + + \begin{figure}[!ht] + \centering + \begin{minipage}{.5\textwidth} + \centering + \includegraphics[height=0.12\textheight]{img/add_cashier.png} + \caption{Ajout d'un caissier} + \end{minipage}% + \begin{minipage}{.5\textwidth} + \centering + \includegraphics[height=0.15\textheight]{img/remove_cashier.png} + \caption{Suppression d'un caissier} + \end{minipage} + \end{figure} + \item[\faitDifferemment] Peut voir la liste des caissiers \textbf{$\Rightarrow$ possibilité d'ouvrir des informations étendues sur un utilisateur, ainsi que de filtrer les utilisateurs (manager et caissiers) mais impossible de tout déselectionner (caissier par défaut)} + + \newpage \item[\fait] Un histogramme présentant l'évolution des sommes totales des ventes journalières de la semaine passée d'un utilisateur \textbf{$\Rightarrow$ accessible au double-clique dans la fenêtre des informations étendues d'un utilisateur} + + \begin{figure}[!ht] + \centering + \includegraphics[height=0.3\textheight]{img/user_info.png} + \caption{Informations étendues sur un utilisateur} + \end{figure} + \item[\fait] Un bouton pour vider tous les champs de saisie \item[\faitDifferemment] Un bouton pour quitter l'application \textbf{$\Rightarrow$ j'ai préféré mettre un bouton pour se déconnecter} \item[\fait] Un bouton pour se mettre en "mode caissier" \end{todolist} \item[\fait] Page de caissier (définit par un identifiant, un nom d'utilisateur, un mot de passe, un nom, un prenom, une date de naissance, une adresse et un code postal) \texttt{/6} + + \begin{figure}[!ht] + \hfill + \subfigure[En tant que Caissier]{\includegraphics[height=0.2\textheight]{img/cashier.png}} + \hfill + \subfigure[En tant que Manager]{\includegraphics[height=0.2\textheight]{img/cashier_as_manager.png}} + \hfill + \caption{Page du Caissier} + \end{figure} + \begin{todolist} \item[\fait] Afficher le stock disponible \begin{todolist} - \item[\faitDifferemment] 4 rayons de chacun au moins 10 articles de votre choix (fruits/légumes, boulangerie, boucherie/poissonnerie ou produits d'entretien) \textbf{$\Rightarrow$ toutes les images sont aux dimensions 50x50 et ont étés converties avec le logiciel Gimp} + \item[\faitDifferemment] 4 rayons de chacun au moins 10 articles de votre choix (fruits/légumes, boulangerie, \\boucherie/poissonnerie ou produits d'entretien) \textbf{$\Rightarrow$ toutes les images sont aux dimensions 50x50 et ont étés converties avec le logiciel Gimp} \item[\faitDifferemment] Au clic sur le produit, l'identifiant, le nom, la quantité en stock et le prix s'affichent \textbf{$\Rightarrow$ tout est affiché directement, pas besoin de cliquer sur le produit, il y a aussi un système de pages pour une meilleur lisibilité (10 éléments par page au maximum)} \end{todolist} + + \newpage \item[\fait] Possibilité de rajouter des produits en stock + + \begin{figure}[!ht] + \centering + \includegraphics[height=0.3\textheight]{img/add_stock.png} + \caption{Ajout d'éléments au stock} + \end{figure} + \item[\fait] Affichage d'un ticket de caisse + + \begin{figure}[!ht] + \centering + \includegraphics[height=0.3\textheight]{img/receipt.png} + \caption{Ticket de caisse avec 2 éléments au panier} + \end{figure} + \begin{todolist} \item[\fait] Date de vente \item[\fait] ID, nom, quantité, prix des produits achetés @@ -184,10 +250,6 @@ \item[\faitDifferemment] Ajout d'autres fonctionnalités \texttt{/1} \begin{todolist} \item[\faitDifferemment] J'ai pas vraiment ajouter une toute nouvelle fonctionnalité, mais améliorer ce qui était demandé pour une plus grande souplesse à l'utilisation (cf. les cases cochés avec des \ding{56}) - % \item Heure de connexion stricte pour les caissiers - % \item Meilleur caissier (avec le plus de ventes sur la journée/semaine précédente par exemple) - % \item Exporter le ticket de caisse en format image - % \item Chiffrer les mots de passes dans la base de donnée \end{todolist} \item[\faitDifferemment] Lisibilité du code \begin{todolist} diff --git a/documentation/img/add_cashier.png b/documentation/img/add_cashier.png new file mode 100644 index 0000000..4712a0f --- /dev/null +++ b/documentation/img/add_cashier.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b87768815f469b8f3e98f20b8b65500650ca3d5520e8430c1bd657e6ce397d9d +size 32727 diff --git a/documentation/img/add_stock.png b/documentation/img/add_stock.png new file mode 100644 index 0000000..1311b85 --- /dev/null +++ b/documentation/img/add_stock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d697b1d646c0861f7ddd6e44e84704549b80ae1e5d2795682527d43ee1e05a5 +size 12219 diff --git a/documentation/img/cashier.png b/documentation/img/cashier.png new file mode 100644 index 0000000..5250133 --- /dev/null +++ b/documentation/img/cashier.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32511035564e6a66db5180c6cbac606b4ae8c219bafd5178aa820b910c935658 +size 169011 diff --git a/documentation/img/cashier_as_manager.png b/documentation/img/cashier_as_manager.png new file mode 100644 index 0000000..34b4adb --- /dev/null +++ b/documentation/img/cashier_as_manager.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b351382d5c27c6532a3f42ccdbb2c706920dde1aab55fac65ef978f7d5d366d +size 150569 diff --git a/documentation/img/login.png b/documentation/img/login.png new file mode 100644 index 0000000..e95f830 --- /dev/null +++ b/documentation/img/login.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23ed3447bdf38ae231e34a7f25e939d790db842cb5a76164449d65551bae42a4 +size 28946 diff --git a/documentation/img/manager.png b/documentation/img/manager.png new file mode 100644 index 0000000..6a49e72 --- /dev/null +++ b/documentation/img/manager.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd23d4007c9fa97713217344883078d94499e893e6bf6cee3b0a12d244c1dae9 +size 34864 diff --git a/documentation/img/receipt.png b/documentation/img/receipt.png new file mode 100644 index 0000000..c211e95 --- /dev/null +++ b/documentation/img/receipt.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6eb347ec1a6c86a49ff12483bdaa60b47eafeff14785853f18a54e549a4a2b8c +size 14005 diff --git a/documentation/img/remove_cashier.png b/documentation/img/remove_cashier.png new file mode 100644 index 0000000..60cbb6f --- /dev/null +++ b/documentation/img/remove_cashier.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:688c019784b54587d2e1be7c06bcb31fea682e2396a9874a3e59bff7aab02f21 +size 11875 diff --git a/documentation/img/user_info.png b/documentation/img/user_info.png new file mode 100644 index 0000000..de15fa4 --- /dev/null +++ b/documentation/img/user_info.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bffb7157e2f4df0e4887dd9252415b7192707d4dd6c1941309feb2195c3bc105 +size 34191 From 574e3eb3574ce050d389f550b184598efc1760f4 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Thu, 2 Dec 2021 16:59:51 +0100 Subject: [PATCH 15/18] add global histogram who show the addition of all the sells of all the cashier on the main manager page --- documentation/img/manager.png | 4 +- main.py | 99 ++++++++++++++++++----------------- stats.py | 25 +++++++-- 3 files changed, 72 insertions(+), 56 deletions(-) diff --git a/documentation/img/manager.png b/documentation/img/manager.png index 6a49e72..15e8309 100644 --- a/documentation/img/manager.png +++ b/documentation/img/manager.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd23d4007c9fa97713217344883078d94499e893e6bf6cee3b0a12d244c1dae9 -size 34864 +oid sha256:7c41fef2a394c4abed4b26fe678dc518027893f143896c305dcc440df6e4039e +size 45442 diff --git a/main.py b/main.py index 1d5617c..f8a0763 100644 --- a/main.py +++ b/main.py @@ -599,7 +599,7 @@ class GesMag: showerror(f"Erreur – {self.nomApp}", "Vous ne pouvez pas accéder à cette interface.") return self.parent.title(f"Manager {manager['nom']} {manager['prenom']} – {self.nomApp}") - self.dimensionsFenetre(self.parent, 580, 310) + self.dimensionsFenetre(self.parent, 580, 530) # Suppresssion de la dernière Frame self.f.destroy() @@ -767,6 +767,44 @@ class GesMag: Button(enfant, text="Supprimer", command=___suppressionUtilisateur).grid(column=0, row=8, columnspan=3, sticky='w') Button(enfant, text="Quitter", command=enfant.destroy).grid(column=0, row=8, columnspan=3, sticky='e') + def __actualisationCanvas(canvas: Canvas, donnees: dict) -> None: + """Affiche l'histogramme des vente d'un utilisateur dans un canvas.""" + if len(donnees) <= 0: + canvas.create_text(10, 10, anchor='w', text="Aucun résultat récemment enregistré") + else: + # Les dates dans le fichier CSV ne sont pas dans l'ordre + # On retire l'ID et le pseudo du dictionnaire si présent dedans + donnees.pop("id", None) + donnees.pop("pseudo", None) + ecart = 10 + couleurs = [ + "CadetBlue3", + "HotPink2", + "IndianRed1", + "MediumPurple2", + "burlywood2", + "brown3", + "chocolate1", + "goldenrod2" + ] + # On récupère la plus grosse vente + maxVente = 0 # par défaut la meilleur vente est de 0 + for prix in donnees.values(): + prix = float(prix) + if prix > maxVente: # si on trouve une valeure plus grande + maxVente = prix + canvas.create_text(3, 190, anchor='w', text="€", font=("Arial", 7)) # affichage de la devise + for date in sorted(donnees.keys()): # on regarde les dates dans l'ordre + # Affichage de la date + canvas.create_text(ecart + 10, 60, anchor='w', text=date, font=("Arial", 8), angle=90) + # Affichage de la barre + hauteur = 190 - (float(donnees[date]) * 100) / maxVente # calcul de la hauteur en fonction de la plus grosse vente + # On fait `- 20` au résultat pour allonger la barre, aussi on met une barre de `2` pixel quand valeur petite + canvas.create_rectangle(ecart, 180, ecart + 15, hauteur - 20 if hauteur < 180 else 178, fill=couleurs.pop()) + # Affichage du montant + canvas.create_text(ecart, 190, anchor='w', text=str(donnees[date]).replace('.', ','), font=("Arial", 7)) + ecart += 33 + def __afficherInformationsUtilisateur(_) -> None: """Permet d'afficher les informations d'un utilisateur""" element = listeUtilisateurs.curselection() @@ -798,55 +836,11 @@ class GesMag: frameSuivi = LabelFrame(enfant, text="Histogramme des ventes", font=self.font) frameSuivi.grid(column=1, row=0, sticky="ns", padx=5) - def ___actualisationCanvas() -> None: - """Affiche l'histogramme des vente d'un utilisateur dans un canvas.""" - donnees = Stats().recuperationDonneesCSV(utilisateur['id']) - if len(donnees) <= 0: - histogramme.create_text(10, 10, anchor='w', text="Aucun résultat récemment enregistré") - else: - # Les dates dans le fichier CSV ne sont pas dans l'ordre - # On retire l'ID et le pseudo du dictionnaire - donnees.pop("id") - donnees.pop("pseudo") - ecart = 10 - couleurs = [ - "CadetBlue3", - "HotPink2", - "IndianRed1", - "MediumPurple2", - "burlywood2", - "brown3", - "chocolate1", - "goldenrod2" - ] + histogrammeUtilisateur = Canvas(frameSuivi, width=270, height=200) + histogrammeUtilisateur.grid() - # On remplace tous les prix `None` par `0.` (au cas où il y est des valeurs vide dans le `CSV`) - for date, prix in donnees.items(): - if prix == None: - donnees[date] = 0. - - # On récupère la plus grosse vente - maxVente = 0 # par défaut la meilleur vente est de 0 - for prix in donnees.values(): - prix = float(prix) - if prix > maxVente: # si on trouve une valeure plus grande - maxVente = prix - - for date in sorted(donnees.keys()): # on regarde les dates dans l'ordre - # Affichage de la date - histogramme.create_text(ecart + 10, 60, anchor='w', text=date, font=("Arial", 8), angle=90) - # Affichage de la barre - hauteur = 190 - (float(donnees[date]) * 100) / maxVente # calcul de la hauteur en fonction de la plus grosse vente - # On fait `- 20` au résultat pour allonger la barre, aussi on met une barre de `2` pixel quand valeur petite - histogramme.create_rectangle(ecart, 180, ecart + 15, hauteur - 20 if hauteur < 180 else 178, fill=couleurs.pop()) - # Affichage du montant - histogramme.create_text(ecart, 190, anchor='w', text=donnees[date], font=("Arial", 7)) - ecart += 33 - - histogramme = Canvas(frameSuivi, width=270, height=200) - histogramme.grid() - - ___actualisationCanvas() + donneesUtilisateur = Stats().recuperationDonneesCSV(utilisateur['id']) + __actualisationCanvas(histogrammeUtilisateur, donneesUtilisateur) Button(enfant, text="Quitter", command=enfant.destroy).grid(column=0, row=1, columnspan=2) @@ -926,6 +920,13 @@ class GesMag: Button(self.f, text="Passer en mode caissier", font=self.font, command=lambda: self._interfaceCaissier(id)).grid(column=0, row=6, columnspan=3, pady=10) + # Histogramme global + histogrammeUtilisateur = Canvas(self.f, width=270, height=200) + histogrammeUtilisateur.grid(column=0, row=7, columnspan=3) + + donneesGlobales = Stats().recuperationDonneesCSV() + __actualisationCanvas(histogrammeUtilisateur, donneesGlobales) + if __name__ == "__main__": """Application "GesMag" pour le module de Programmation d'interfaces (2021-2022)""" """ diff --git a/stats.py b/stats.py index 5975e35..eb6cc57 100644 --- a/stats.py +++ b/stats.py @@ -99,14 +99,29 @@ class Stats(): else: fichier.writerow([utilisateurID, "Aucune ventes enregistrée"]) - def recuperationDonneesCSV(self, utilisateurID: int) -> dict: - """Renvoie les informations contenu dans le fichier `CSV` globale.""" + def recuperationDonneesCSV(self, utilisateurID: int = None) -> dict: + """ + Renvoie les informations contenu dans le fichier `CSV` globale. + + Si un argument est renseignée pour `utilisateurID` alors seul l'utilisateur + renseigné sera renvoyé. + """ self.miseAJourDatesCSV() # met à jour les dates du fichier `CSV` + with open("stats.csv", 'r') as f: fichier = list(csv.DictReader(f)) # lecture du fichier sous forme d'une liste de dictionnaire - for utilisateur in fichier: # on regarde tous les utilisateurs stockés dans le fichier - if utilisateur["id"] == str(utilisateurID): # si utilisateur trouvé - return utilisateur # renvoie des infos de l'utilisateur + if utilisateurID == None: # si on renvoie les stats globales + dates = dict.fromkeys(self.datesDisponibles(), 0.) + for date in dates: + for utilisateur in fichier: + if utilisateur[date] == None: # On remplace tous les prix `None` par `0.` + utilisateur[date] = 0. + dates[date] += float(utilisateur[date]) + return dates + else: # si on renvoie les stats spécifique à un utilisateur + for utilisateur in fichier: # on regarde tous les utilisateurs stockés dans le fichier + if utilisateur["id"] == str(utilisateurID): # si utilisateur trouvé + return utilisateur # renvoie des infos de l'utilisateur return {} # ne retourne rien si l'utilisateur n'était pas présent dans le fichier def miseAJourDatesCSV(self) -> None: From b1261ca4c04624682313da08e39e19b0a00f4ec8 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Thu, 2 Dec 2021 17:54:14 +0100 Subject: [PATCH 16/18] update documentation with latest image and code --- documentation/documentation.pdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index 7cd7d7d..d7f3c13 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b15d05e6f9c710673b7ee8f66a9a355645c68d6e27185860a379023a803b721 -size 985588 +oid sha256:b6e9e36a9f20bd6382e7e1e605eef859a7e70913f1d41d9ac36934cba8d7e03a +size 998977 From 9aeb7601157daf129522f6fa83ea79ebca6adb45 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Thu, 2 Dec 2021 20:21:51 +0100 Subject: [PATCH 17/18] Fix issue when missing value in csv file for the histogram --- main.py | 8 +++++--- stats.py | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index f8a0763..042d26a 100644 --- a/main.py +++ b/main.py @@ -790,6 +790,7 @@ class GesMag: # On récupère la plus grosse vente maxVente = 0 # par défaut la meilleur vente est de 0 for prix in donnees.values(): + print(prix) prix = float(prix) if prix > maxVente: # si on trouve une valeure plus grande maxVente = prix @@ -921,11 +922,12 @@ class GesMag: Button(self.f, text="Passer en mode caissier", font=self.font, command=lambda: self._interfaceCaissier(id)).grid(column=0, row=6, columnspan=3, pady=10) # Histogramme global - histogrammeUtilisateur = Canvas(self.f, width=270, height=200) - histogrammeUtilisateur.grid(column=0, row=7, columnspan=3) + Label(self.f, text="Histogramme\nventes totales", font=self.font).grid(column=0, row=7, sticky='e') + histogrammeGlobale = Canvas(self.f, width=270, height=200) + histogrammeGlobale.grid(column=1, row=7, columnspan=2) donneesGlobales = Stats().recuperationDonneesCSV() - __actualisationCanvas(histogrammeUtilisateur, donneesGlobales) + __actualisationCanvas(histogrammeGlobale, donneesGlobales) if __name__ == "__main__": """Application "GesMag" pour le module de Programmation d'interfaces (2021-2022)""" diff --git a/stats.py b/stats.py index eb6cc57..846135f 100644 --- a/stats.py +++ b/stats.py @@ -114,13 +114,16 @@ class Stats(): dates = dict.fromkeys(self.datesDisponibles(), 0.) for date in dates: for utilisateur in fichier: - if utilisateur[date] == None: # On remplace tous les prix `None` par `0.` + if utilisateur[date] == None: # on remplace les date sans prix par `0.` utilisateur[date] = 0. dates[date] += float(utilisateur[date]) return dates else: # si on renvoie les stats spécifique à un utilisateur for utilisateur in fichier: # on regarde tous les utilisateurs stockés dans le fichier if utilisateur["id"] == str(utilisateurID): # si utilisateur trouvé + for date, prix in utilisateur.items(): # on remplace les date sans prix par `0.` + if prix == None: + utilisateur[date] = 0. return utilisateur # renvoie des infos de l'utilisateur return {} # ne retourne rien si l'utilisateur n'était pas présent dans le fichier From d8f2b84d822c454a6c73e71e1deda120ad23e0b6 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Thu, 2 Dec 2021 21:37:48 +0100 Subject: [PATCH 18/18] update picture (adding label to the histogram) --- documentation/documentation.pdf | 4 ++-- documentation/img/manager.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/documentation.pdf b/documentation/documentation.pdf index d7f3c13..a032c3d 100644 --- a/documentation/documentation.pdf +++ b/documentation/documentation.pdf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6e9e36a9f20bd6382e7e1e605eef859a7e70913f1d41d9ac36934cba8d7e03a -size 998977 +oid sha256:92a6d9b72f16d3a198466882bf9abe081082a0caf59c286f4ed896e55562d9bc +size 998425 diff --git a/documentation/img/manager.png b/documentation/img/manager.png index 15e8309..4ff7db4 100644 --- a/documentation/img/manager.png +++ b/documentation/img/manager.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c41fef2a394c4abed4b26fe678dc518027893f143896c305dcc440df6e4039e -size 45442 +oid sha256:803890ffd0331bf4c240cd962c7b2fb6539d0c083c42f5e4d4e8fbe96fe92f1c +size 43950