diff --git a/main.py b/main.py index a688ae9..cf8d620 100644 --- a/main.py +++ b/main.py @@ -1,986 +1,5 @@ # main.py - -from logs import * -create_log_directory() -log_action(f"Importation du fichier 'logs.py'", success=True) -import sys -log_action(f"Importation de la biblioteque 'sys'", success=True) -import csv -log_action(f"Importation de la biblioteque 'csv'", success=True) -import os -log_action(f"Importation de la biblioteque 'os'", success=True) -from PyQt5.QtWidgets import ( - QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout, - QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget, - QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, - QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser) -log_action(f"Importation de la biblioteque 'PyQt5.QtWidgets' avec comme fonction 'QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout,QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget,QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser'", success=True) -from PyQt5.QtGui import (QPixmap, QIcon) -log_action(f"Importation de la biblioteque 'PyQt5.QtGui' avec comme fonction 'QPixmap, QIcon'", success=True) -from PyQt5.QtCore import Qt, QSettings -log_action(f"Importation de la biblioteque 'PyQt5.QtCore' avec comme fonction 'Qt, QSettings'", success=True) -from PyQt5.QtPrintSupport import QPrinter, QPrintDialog -log_action(f"Importation de la biblioteque 'PyQt5.QtPrintSupport' avec comme fonction 'QPrinter, QPrintDialog'", success=True) -from reportlab.pdfgen import canvas -log_action(f"Importation de la biblioteque 'reportlab.pdfgen' avec comme fonction 'canvas'", success=True) -from reportlab.lib.pagesizes import letter -log_action(f"Importation de la biblioteque 'reportlab.lib.pagesizes' avec comme fonction 'letter'", success=True) -from library_logic import * -log_action(f"Importation du fichier 'library_logic.py'", success=True) -from user_logic import * -log_action(f"Importation du fichier 'user_logic.py'", success=True) -from dialog_logic import * -log_action(f"Importation du fichier 'dialog_logic.py'", success=True) - -def load_stylesheet(file_path): - with open(file_path, 'r') as file: - return file.read() - -class LibraryApp(QMainWindow): - def __init__(self): - super().__init__() - - # Set window properties - self.setWindowTitle("Jobi - Gestionnaire de bibliothèque") - self.setGeometry(100, 100, 800, 500) - self.setWindowIcon(QIcon('icon.png')) - - # Create a central widget to hold the layout - self.central_widget = QWidget() - self.setCentralWidget(self.central_widget) - - # Apply CSS styles to certain components - self.setStyleSheet(load_stylesheet('content\\css\\style.css')) - - # Create a main layout for the central widget - self.layout = QVBoxLayout(self.central_widget) - - # Determine the current directory - current_dir = os.path.dirname(os.path.realpath(__file__)) - - self.file_path = None - self.file_path_book = None - self.file_path_user = None - self.file_path_take = None - - # Display the logo in the banner - self.logo_label = QLabel(self) - self.pixmap = QPixmap(os.path.join(current_dir, 'icon.png')) - self.logo_label.setPixmap(self.pixmap) - - self.settings = QSettings("Jobi", "gestion_library_app") - - self.library = Library() - self.user_gestion = User_gestion() - - # Create a tab widget without specifying geometry - self.tab_widget = QTabWidget(self.central_widget) - self.tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - - # Books Tab - self.books_tab = QWidget() - books_layout = QVBoxLayout() - self.setup_search_section() - self.setup_table("book_table",7,["ID", "Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Disponibles"]) - self.book_table.itemChanged.connect(self.update_book_info) - books_layout.addWidget(self.book_table) - self.books_tab.setLayout(books_layout) - self.tab_widget.addTab(self.books_tab, "Gestion des Livres") - - # Users Tab - self.users_tab = QWidget() - user_layout = QVBoxLayout() - self.setup_user_tab() - self.user_table.itemChanged.connect(self.update_user_info) - self.users_tab.setLayout(user_layout) - self.tab_widget.addTab(self.users_tab, "Gestion des Utilisateurs") - - # Take Tab - self.take_tab = QWidget() - take_layout = QVBoxLayout() - self.setup_take_tab() - self.take_tab.setLayout(take_layout) - self.tab_widget.addTab(self.take_tab, "Gestion des Empreints") - - # Set up stretch factors to prioritize the content in the layout - self.layout.addWidget(self.tab_widget, stretch=1) - - # Load saved CSV data - self.load_saved_csv() - - # Add the bottom layout to the central layout - self.layout.addLayout(self.setup_bottom_layout()) - - # Initialize bottom_layout as an instance attribute - self.bottom_layout = None - - self.tab_widget.currentChanged.connect(self.onTabChanged) - - self.create_menu() - - #Zone de text pour n'import quelle fonctionnalisté -# self.text_browser = QTextBrowser(self) -# books_layout.addWidget(self.text_browser) - - def supprimer_logs(self): - - dossier= "data/logs/" - - # Vérifier si le dossier existe - if not os.path.exists(dossier): - log_action(f"Le dossier '{dossier}' n'existe pas.", False, "Dossier introuvable") - QMessageBox.warning(self, "Dossier introuvable","Le dossier '{dossier}' n'existe pas.") - return - - # Liste tous les fichiers dans le dossier - fichiers = os.listdir(dossier) - - # Parcourir tous les fichiers - for fichier in fichiers: - # Vérifier si le fichier est un fichier .log - if fichier.endswith(".log"): - chemin_fichier = os.path.join(dossier, fichier) - # Supprimer le fichier - os.remove(chemin_fichier) - log_action(f"Fichier '{fichier}' supprimé avec succès.", True) - QMessageBox.information(self, "Tout les fichiers ont été supprimé avec succès", "Le cache à bien été vidé") - - def toggle_fullscreen(self): - if self.isFullScreen(): - self.showNormal() - else: - self.showFullScreen() - - def create_menu(self): - # Menu Fichier - file_menu = self.menuBar().addMenu("Fichier") - - open_action = QAction(QIcon(), "Importer", self) - open_action.setShortcut("Ctrl+O") - open_action.triggered.connect(self.import_from_csv) - log_action("Importation d'un fichier depuis le menu fichier") - file_menu.addAction(open_action) - -# save_action = QAction(QIcon(), "Enregistrer", self) -# save_action.setShortcut("Ctrl+S") -# save_action.triggered.connect(self.) -# file_menu.addAction(save_action) - - save_as_action = QAction(QIcon(), "Enregistrer sous", self) - save_as_action.setShortcut("Ctrl+Shift+S") - save_as_action.triggered.connect(self.export_to_csv) - log_action("Exportation du fichier depuis le menu fichier") - file_menu.addAction(save_as_action) - - file_menu.addSeparator() - - print_action = QAction(QIcon(), "Imprimer toute la librairie", self) - print_action.setShortcut("Ctrl+P") - print_action.triggered.connect(self.print_all_files) - log_action("Impression de la librairie depuis le menu fichier") - file_menu.addAction(print_action) - - print_action = QAction(QIcon(), "Imprimer un livre par ID", self) - print_action.setShortcut("Ctrl+Shift+p") - print_action.triggered.connect(self.print_selected_file) - log_action("Impression d'un livre depuis le menu fichier") - file_menu.addAction(print_action) - - file_menu.addSeparator() - - quit_action = QAction(QIcon(), "Quitter", self) - quit_action.setShortcut("Ctrl+Q") - quit_action.triggered.connect(self.close) - log_action("Fermeture de l'application depuis le menu fichier") - file_menu.addAction(quit_action) - - - # Menu Option - option_menu = self.menuBar().addMenu("Option") - - fullscreen = QAction(QIcon(), "Pleine écran", self) - fullscreen.setShortcut("F11") - fullscreen.triggered.connect(self.toggle_fullscreen) - log_action("Mise en pleine écrant/Mise en fenétré") - option_menu.addAction(fullscreen) - - file_menu.addSeparator() - - vider_cache = QAction(QIcon(), "Vider le cache", self) - vider_cache.setShortcut("Ctrl+Shift+d") - vider_cache.triggered.connect(self.supprimer_logs) - log_action("Suppression logs") - option_menu.addAction(vider_cache) - - # Menu Édition -# edit_menu = self.menuBar().addMenu("Édition") -# -# undo_action = QAction(QIcon(), "Annuler", self) -# undo_action.setShortcut("Ctrl+Z") -# undo_action.triggered.connect(self.text_edit.undo) -# edit_menu.addAction(undo_action) -# -# redo_action = QAction(QIcon(), "Rétablir", self) -# redo_action.setShortcut("Ctrl+Y") -# redo_action.triggered.connect(self.text_edit.redo) -# edit_menu.addAction(redo_action) -# -# copy_action = QAction(QIcon(), "Copier", self) -# copy_action.setShortcut("Ctrl+C") -# copy_action.triggered.connect(self.text_edit.copy) -# edit_menu.addAction(copy_action) -# -# paste_action = QAction(QIcon(), "Coller", self) -# paste_action.setShortcut("Ctrl+V") -# paste_action.triggered.connect(self.text_edit.paste) -# edit_menu.addAction(paste_action) - - def print_all_files(self): - QMessageBox.information(self, "En développement", "Cette fonctionnalité est en cours de développement") -# if self.tab_widget.currentIndex()==0: -# # Imprimer tous les livres -# self.print_files(self.library.books) - - def print_selected_file(self): - if self.tab_widget.currentIndex()==0: - # Demander à l'utilisateur d'entrer l'ID du livre - book_id, ok = QInputDialog.getText(self, 'Entrer l\'ID du livre', 'ID du livre:') - if ok: - if book_id: - book = self.library.get_book_by_id(book_id) - if book: - self.print_files([book]) - else: - self.text_browser.setText(f"Aucun livre trouvé avec l'ID {book_id}") - else: - self.text_browser.setText("Veuillez spécifier un ID de livre") - - def print_files(self, books): - if self.tab_widget.currentIndex() == 0: - # Demander à l'utilisateur où enregistrer le fichier PDF - file_path, _ = QFileDialog.getSaveFileName(self, 'Enregistrer le PDF', '', 'PDF Files (*.pdf)') - if not file_path: - return # L'utilisateur a annulé la sauvegarde - - try: - # Créer un fichier PDF avec la bibliothèque reportlab - pdf = canvas.Canvas(file_path, pagesize=letter) - pdf.setFont("Helvetica", 12) - - # Imprimer les détails des livres dans le PDF - for book in books: - pdf.drawString(100, 750, f"Book ID: {book.book_id}") - pdf.drawString(100, 730, f"Title: {book.title}") - pdf.drawString(100, 710, f"Author: {book.author}") - pdf.drawString(100, 690, f"ISBN: {book.isbn}") - pdf.drawString(100, 670, f"Total Copies: {book.total_copies}") - pdf.drawString(100, 650, f"Available Copies: {book.available_copies}") - pdf.drawString(100, 630, f"Publisher: {book.publisher}") - pdf.drawString(100, 610, "\n") - - # Sauvegarder et fermer le fichier PDF - pdf.save() - - # Afficher une boîte de dialogue pour indiquer que le PDF a été créé avec succès - QMessageBox.information(self, "PDF Créé", "Le PDF a été créé avec succès.", QMessageBox.Ok) - - # Journaliser l'action - log_action(f"Création du fichier PDF réussie : {file_path}", success=True) - except Exception as e: - # Journaliser l'erreur en cas d'échec - log_action(f"Erreur lors de la création du fichier PDF : {str(e)}", success=False) - - - def onTabChanged(self, index): - if index == 0: - new_text = 'Ajouter un livre' - self.btn_open_add_book_dialog.setText(new_text) - log_action('Changement d\'onglet vers \'Ajouter un livre\'', success=True) - self.setWindowTitle(f"Jobi - {self.file_path_book if self.file_path_book!=None else 'Gestionnaire de bibliothèque'}") - log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_book if self.file_path_book!=None else 'Gestionnaire de bibliothèque'}'") - elif index == 1: - new_text = 'Ajouter un utilisateur' - self.btn_open_add_book_dialog.setText(new_text) - log_action('Changement d\'onglet vers \'Ajouter un utilisateur\'', success=True) - self.setWindowTitle(f"Jobi - {self.file_path_user if self.file_path_user!=None else 'Gestionnaire de bibliothèque'}") - log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_user if self.file_path_user!=None else 'Gestionnaire de bibliothèque'}'") - elif index == 2: - new_text = 'Ajouter un emprunt' - self.btn_open_add_book_dialog.setText(new_text) - log_action('Changement d\'onglet vers \'Ajouter un emprunt\'', success=True) - self.setWindowTitle(f"Jobi - {self.file_path_take if self.file_path_take!=None else 'Gestionnaire de bibliothèque'}") - log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_take if self.file_path_take!=None else 'Gestionnaire de bibliothèque'}'") - - def closeEvent(self, event): - # Sauvegarde du fichier CSV avant de fermer l'application - log_action("Fermeture de l'application. Tentative de sauvegarde...") - try: - save_directory = os.path.join("data", "save") - os.makedirs(save_directory, exist_ok=True) # Création du répertoire de sauvegarde s'il n'existe pas - save_path = os.path.join(save_directory, "saved_books.csv") - self.export_save_to_csv(save_path, librairy.books) # Utilisation de la fonction d'exportation existante - except Exception as e: - log_action(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) - print(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) - - def load_saved_csv(self): - saved_file_path = os.path.join("data", "save", "saved_books.csv") - if os.path.exists(saved_file_path): - self.file_path = saved_file_path - books = self.library.import_from_csv(saved_file_path) - print(books) # Ajoutez ce print pour vérifier les données chargées depuis le CSV - self.update_book_table() - else: - log_action("Aucun fichier sauvegardé trouvé.", False) - - - def open_add_book_dialog(self): - dialog = AddBookDialog(self) - dialog.library_app = self - dialog.exec_() - - - def setup_search_section(self): - search_layout = QHBoxLayout() - - self.search_label = QLabel("Recherche:") - self.entry_search = QLineEdit() - self.entry_search.setObjectName("entry_search") - - self.entry_search.textChanged.connect(self.search_books) - search_layout.addWidget(self.search_label) - search_layout.addWidget(self.entry_search) - - self.search_type_label = QLabel("Type :") - self.search_combobox = QComboBox() - self.search_combobox.setObjectName("search_combobox") - - search_options = ["Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Diponibles", "ID"] - self.search_combobox.addItems(search_options) - self.search_combobox.currentIndexChanged.connect(self.search_books) - search_layout.addWidget(self.search_type_label) - search_layout.addWidget(self.search_combobox) - - self.layout.addLayout(search_layout) - - def setup_table(self, val_name, colomn_nbr, colomn_names): - # Utilisez setattr pour créer dynamiquement un attribut d'instance avec le nom spécifié - setattr(self, val_name, QTreeWidget()) - - # Utilisez l'attribut d'instance pour accéder à votre QTreeWidget - getattr(self, val_name).setColumnCount(colomn_nbr) - getattr(self, val_name).setHeaderLabels(colomn_names) -# getattr(self, val_name).setSortingEnabled(True) # A resoudre pour eviter le mauvais rangement - - header = getattr(self, val_name).header() - header.setSectionResizeMode(QHeaderView.ResizeToContents) - - # Ajoutez le QTreeWidget au layout de votre classe - self.layout.addWidget(getattr(self, val_name)) - - # Rendre tous les éléments éditables dans le tableau - getattr(self, val_name).setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked | QAbstractItemView.EditKeyPressed) - - # Activer la détection du clic droit pour le menu contextuel - getattr(self, val_name).setContextMenuPolicy(Qt.CustomContextMenu) - getattr(self, val_name).customContextMenuRequested.connect(self.show_user_context_menu) - - - def setup_user_tab(self): - # Set up the user management components in the users tab - user_layout = QVBoxLayout() - - # Set up the user management table and related components in the users tab - self.setup_table("user_table", 6,["ID", "Nom", "Prénom", "Email", "Addresse", "Nombre d'Empreint"]) - self.user_table.itemChanged.connect(self.update_user_info) - - user_layout.addWidget(self.user_table) - - self.users_tab.setLayout(user_layout) - - def show_user_context_menu(self, pos): - menu = QMenu(self) - copy_action = menu.addAction("Copier") - delete_action = menu.addAction("Supprimer") - modify_action = menu.addAction("Modifier") - - action = menu.exec_(self.user_table.mapToGlobal(pos)) - if action == copy_action: - selected_item = self.user_table.currentItem() - if selected_item is not None: - clipboard = QApplication.clipboard() - clipboard.setText(selected_item.text(self.user_table.currentColumn())) - elif action == modify_action: - selected_item = self.user_table.currentItem() - if selected_item is not None: - self.edit_cell(selected_item) - elif action == delete_action: - selected_item = self.user_table.currentItem() - if selected_item is not None: - log_action(f"Tentative de suppression de l'utilisateur avec l'ID={selected_item.text(0)}", success=True) - # Handle deletion confirmation and process similarly to books if needed - - def update_user_info(self): - try: - selected_items = self.user_table.selectedItems() - for item in selected_items: - user_id = int(item.text(0)) - user = self.user_gestion.get_user_by_id(user_id) - - column = self.user_table.currentColumn() - column_name = self.user_table.headerItem().text(column) # Récupération du nom de la colonne - - # Mettre à jour les détails de l'utilisateur selon la colonne - # Example: if column == 1: user.name = new_value - # ... - - # Appel à la méthode de la classe Library pour mettre à jour les détails de l'utilisateur - # self.library.update_user_details(user) - # ... - - # Log action pour indiquer la modification de la colonne - log_action(f"Modification sur la colonne '{column_name}': 'old_value' -> 'new_value'", success=True) - - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de la mise à jour des détails de l'utilisateur : {str(e)}", success=False) - print("Une erreur s'est produite :", e) - - def setup_take_table(self): - self.take_table = QTreeWidget() - self.take_table.setColumnCount(3) # Adjust the number of columns as needed - self.take_table.setHeaderLabels(["ID", "ID du livre", "Titre", "Auteur", "ISBN", "ID de l'empreinteur", "Nom de l'empreinteur", "Prénom de l'empreinteur"]) - header = self.take_table.header() - header.setSectionResizeMode(QHeaderView.ResizeToContents) - self.layout.addWidget(self.take_table) - - # Rendre tous les éléments éditables dans le tableau - self.take_table.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked | QAbstractItemView.EditKeyPressed) - - # Activer la détection du clic droit pour le menu contextuel - self.take_table.setContextMenuPolicy(Qt.CustomContextMenu) - self.take_table.customContextMenuRequested.connect(self.show_take_context_menu) - - def setup_take_tab(self): - # Set up the user management components in the users tab - take_layout = QVBoxLayout() - - # Set up the user management table and related components in the users tab - self.setup_take_table() - self.take_table.itemChanged.connect(self.update_take_info) - - take_layout.addWidget(self.take_table) - - self.take_tab.setLayout(take_layout) - - def show_take_context_menu(self, pos): - menu = QMenu(self) - copy_action = menu.addAction("Copier") - delete_action = menu.addAction("Supprimer") - modify_action = menu.addAction("Modifier") - - action = menu.exec_(self.take_table.mapToGlobal(pos)) - if action == copy_action: - selected_item = self.take_table.currentItem() - if selected_item is not None: - clipboard = QApplication.clipboard() - clipboard.setText(selected_item.text(self.take_table.currentColumn())) - elif action == modify_action: - selected_item = self.take_table.currentItem() - if selected_item is not None: - self.edit_take_cell(selected_item) - elif action == delete_action: - selected_item = self.take_table.currentItem() - if selected_item is not None: - log_action(f"Tentative de suppression de l'empreint avec l'ID={selected_item.text(0)}", success=True) - # Handle deletion confirmation and process similarly to books if needed - - def update_take_info(self): - try: - selected_items = self.take_table.selectedItems() - for item in selected_items: - take_id = int(item.text(0)) - take = self.library.get_take_by_id(take_id) - - column = self.user_table.currentColumn() - column_name = self.user_table.headerItem().text(column) # Récupération du nom de la colonne - - # Mettre à jour les détails de l'utilisateur selon la colonne - # Example: if column == 1: take.name = new_value - # ... - - # Appel à la méthode de la classe Library pour mettre à jour les détails de l'empreint - # self.library.update_take_details(take) - # ... - - # Log action pour indiquer la modification de la colonne - log_action(f"Modification sur la colonne '{column_name}': 'old_value' -> 'new_value'", success=True) - - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de la mise à jour des détails de l'empreint : {str(e)}", success=False) - print("Une erreur s'est produite :", e) - - def setup_borrow_return_remove_sections(self): - # Add the borrow/return/remove sections directly to the main layout - self.setup_borrow_return_remove_buttons() - self.setup_import_export_buttons() - - def setup_borrow_return_remove_buttons(self): - borrow_return_layout = QHBoxLayout() - - self.borrow_label = QLabel("ID à emprunter:") - self.return_label = QLabel("ID à retourner:") - self.btn_take = QPushButton("Emprunter") - self.btn_return = QPushButton("Retourner") - - self.entry_take = QLineEdit() - self.entry_return = QLineEdit() - - self.btn_take.clicked.connect(self.take_book) - - borrow_return_layout.addWidget(self.borrow_label) - borrow_return_layout.addWidget(self.entry_take) - - borrow_return_layout.addWidget(self.btn_take) - - borrow_return_layout.addWidget(self.return_label) - borrow_return_layout.addWidget(self.entry_return) - - - self.btn_return.clicked.connect(self.return_book) - borrow_return_layout.addWidget(self.btn_return) - - self.remove_label = QLabel("ID à supprimer:") - self.entry_remove = QLineEdit() - borrow_return_layout.addWidget(self.remove_label) - borrow_return_layout.addWidget(self.entry_remove) - - self.btn_remove = QPushButton("Supprimer livre") - self.btn_remove.clicked.connect(self.remove_book) - borrow_return_layout.addWidget(self.btn_remove) - - # Add the borrow/return/remove layout to the main layout - self.layout.addLayout(borrow_return_layout) - - def setup_bottom_layout(self): - # Create a layout for the buttons at the bottom - self.bottom_layout = QHBoxLayout() - - # Add the borrow, return, and remove buttons to the bottom layout - self.setup_borrow_return_remove_buttons() - - # Add the import and export buttons to the bottom layout - self.setup_import_export_buttons() - - # Add stretch factor to push buttons to the right - self.bottom_layout.addStretch(1) - - return self.bottom_layout - - def setup_import_export_buttons(self): - self.import_export_layout = QHBoxLayout() - - self.btn_import = QPushButton("Importer depuis CSV") - self.btn_import.clicked.connect(self.import_from_csv) - self.import_export_layout.addWidget(self.btn_import) - -# self.current = QPushButton("onglet") -# self.current.clicked.connect(self.currentonglet) -# self.import_export_layout.addWidget(self.current) - - self.btn_export = QPushButton("Exporter vers CSV") - self.btn_export.clicked.connect(self.export_to_csv) - self.import_export_layout.addWidget(self.btn_export) - - self.btn_open_add_book_dialog = QPushButton("Ajouter un livre") - self.btn_open_add_book_dialog.clicked.connect(self.open_add_book_dialog) - self.import_export_layout.addWidget(self.btn_open_add_book_dialog) - - self.layout.addLayout(self.import_export_layout) - self.layout.update() - - # Ajoute cette méthode dans AddBookDialog - def set_library_app_reference(self, library_app): - self.library_app = library_app - -# def currentonglet(self): -# print(self.tab_widget.currentIndex()) - - # Fonction pour afficher le menu contextuel lors du clic droit - def show_context_menu(self, pos): - menu = QMenu(self) - copy_action = menu.addAction("Copier") - delete_action = menu.addAction("Supprimer") - modify_action = menu.addAction("Modifier") - - action = menu.exec_(self.book_table.mapToGlobal(pos)) - if action == copy_action: - selected_item = self.book_table.currentItem() - if selected_item is not None: - clipboard = QApplication.clipboard() - clipboard.setText(selected_item.text(self.book_table.currentColumn())) - elif action == modify_action: - selected_item = self.book_table.currentItem() - if selected_item is not None: - self.edit_cell(selected_item) - elif action == delete_action: - selected_item = self.book_table.currentItem() - if selected_item is not None: - log_action(f"Tentative de suppression de l'élément avec l'ID={selected_item.text(0)}", success=True) - # Vérifie si la case à cocher "Ne plus afficher" est déjà cochée - never_show_checked = self.settings.value("NeverShowConfirmation", False, type=bool) - if not never_show_checked: - confirmation_dialog = ConfirmationDialog("Êtes-vous sûr de vouloir supprimer cet élément ?") - result = confirmation_dialog.exec_() - if result == QDialog.Accepted: - should_hide_dialog = confirmation_dialog.never_show_checkbox.isChecked() - if should_hide_dialog: - self.settings.setValue("NeverShowConfirmation", True) # Enregistrer le choix utilisateur - self.delete_selected_item() # Appel à la fonction de suppression - confirmation_dialog.deleteLater() # Supprimer la boîte de dialogue après utilisation - else: - self.delete_selected_item() # Si "Ne plus afficher" est déjà coché, supprime directement l'élément - - - def update_book_info(self): - try: - selected_items = self.book_table.selectedItems() - for item in selected_items: - book_id = int(item.text(0)) - book = self.library.get_book_by_id(book_id) - - column = self.book_table.currentColumn() - column_name = self.book_table.headerItem().text(column) # Récupération du nom de la colonne - - # Mettre à jour les détails du livre selon la colonne - if column == 1: # Supposez que la colonne 1 correspond au titre du livre - old_value = book.title - new_value = item.text(column) - book.title = new_value - elif column == 2: # Colonne pour l'auteur - old_value = book.author - new_value = item.text(column) - book.author = new_value - elif column == 3: # Colonne pour la maison d'édition - old_value = book.publisher - new_value = item.text(column) - book.publisher = new_value - elif column == 4: # Colonne pour l'ISBN - old_value = book.isbn - new_value = item.text(column) - book.isbn = new_value - elif column == 5: # Colonne pour le nombre total d'exemplaires - old_value = book.total_copies - new_value = int(item.text(column)) - book.total_copies = new_value - elif column == 6: # Colonne pour le nombre d'exemplaires disponibles - old_value = book.available_copies - new_value = int(item.text(column)) - book.available_copies = new_value - # Mise à jour du nombre d'exemplaires disponibles si nécessaire - # book.available_copies = ... (calcul pour déterminer les exemplaires disponibles) - - # Appel à la méthode de la classe Library pour mettre à jour les détails du livre - self.library.update_book_details(book) - self.update_book_table() - - # Log action pour indiquer la modification de la colonne - log_action(f"Modification sur la colonne '{column_name}': '{old_value}' -> '{new_value}'", success=True) - - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de la mise à jour des détails du livre : {str(e)}", success=False) - print("Une erreur s'est produite :", e) - - - def update_book_table(self, books=None): - self.book_table.clear() - if not books: - books = self.library.display_books() - - for book in books: - item = QTreeWidgetItem(self.book_table) - item.setText(0, str(book.book_id)) - item.setText(1, book.title) - item.setText(2, book.author) - item.setText(3, str(book.publisher)) - item.setText(4, book.isbn) - item.setText(5, str(book.total_copies)) - item.setText(6, str(book.available_copies)) - - # Rendre tous les éléments de cet item éditables - for column in range(self.book_table.columnCount()): - item.setFlags(item.flags() | Qt.ItemIsEditable) - - def update_user_table(self, users=None): - self.user_table.clear() - if not users: - users = self.user_gestion.display_users() - - for user in users: - item = QTreeWidgetItem(self.user_table) - item.setText(0, str(user.user_id)) - item.setText(1, user.fname) - item.setText(2, user.name) - item.setText(3, str(user.mail)) - item.setText(4, user.take) - - # Rendre tous les éléments de cet item éditables - for column in range(self.user_table.columnCount()): - item.setFlags(item.flags() | Qt.ItemIsEditable) - - def take_book(self): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - book_id = self.entry_take.text() - if book_id=='': - log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") - QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") - - else: - success, message = self.library.take_book_by_id(book_id) - if success: - # Enregistrement de l'action dans les logs - log_action(f"Emprunt d'un livre avec l'ID={book_id}", success=True) - QMessageBox.information(self, "Emprunt", message) - self.update_book_table() - else: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de l'emprunt d'un livre avec l'ID={book_id}: {str(message)}", success=False) - QMessageBox.warning(self, "Emprunt impossible", message) - - def return_book(self): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - book_id = self.entry_return.text() - if book_id=='': - log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") - QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") - - else: - success, message = self.library.return_book_by_id(book_id) - if success: - # Enregistrement de l'action dans les logs - log_action(f"Retour d'un livre avec l'ID={book_id}", success=True) - QMessageBox.information(self, "Retour", message) - self.update_book_table() - else: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors du retour d'un livre avec l'ID={book_id}: {str(message)}", success=False) - QMessageBox.warning(self, "Retour impossible", message) - - def remove_book(self): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - book_id = self.entry_remove.text() - if book_id=='': - log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") - QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") - - else: - success, message = self.library.remove_book_by_id(book_id) - if success: - # Enregistrement de l'action dans les logs - log_action(f"Suppression d'un livre avec l'ID={book_id}", success=True) - QMessageBox.information(self, "Suppression", message) - self.update_book_table() - else: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de la suppression d'un livre avec l'ID={book_id}: {str(message)}", success=False) - QMessageBox.warning(self, "Suppression impossible", message) - - def delete_selected_item(self): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - selected_item = self.book_table.currentItem() - if selected_item is not None: - book_id = selected_item.text(0) - # Utilisez la fonction remove_book_by_id de la bibliothèque pour supprimer l'élément - success, message = self.library.remove_book_by_id(book_id) - if success: - # Enregistrement de l'action dans les logs - log_action(f"Suppression d'un livre avec l'ID={book_id}", success=True) - QMessageBox.information(self, "Suppression", message) - self.update_book_table() # Mettre à jour l'interface utilisateur après la suppression - else: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de la suppression d'un livre avec l'ID={book_id}: {message}", success=False) - QMessageBox.warning(self, "Suppression impossible", message) - - def import_from_csv(self): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - new_book_id = None # Initialisation de new_book_id en dehors du bloc try - - try: - file_path, _ = QFileDialog.getOpenFileName(self, "Sélectionner un fichier CSV", "", "CSV Files (*.csv)") - self.setWindowTitle(f"Jobi - {file_path}") - log_action(f"Changement du Windows Title par 'Jobi - {file_path}'") - self.file_path_book = file_path - log_action(f"Modification de la banniere superieur par : {file_path}", success=False) - if file_path: - success = self.library.import_from_csv(file_path) - if success: - # Enregistrement de l'action dans les logs - log_action("Importation réussie depuis un fichier CSV", success=True) - QMessageBox.information(self, "Importation réussie", "Les livres ont été importés avec succès depuis le fichier CSV.") - self.update_book_table() - else: - # Enregistrement de l'erreur dans les logs - log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) - # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - - if current_tab_index == 1: - new_user_id = None # Initialisation de new_user_id en dehors du bloc try - - try: - file_path, _ = QFileDialog.getOpenFileName(self, "Sélectionner un fichier CSV", "", "CSV Files (*.csv)") - self.setWindowTitle(f"Jobi - {file_path}") - self.file_path_user = file_path - log_action(f"Modification de la banniere superieur par : {file_path}", success=False) - if file_path: - success = self.user_gestion.import_from_csv(file_path) - if success: - # Enregistrement de l'action dans les logs - log_action("Importation réussie depuis un fichier CSV", success=True) - QMessageBox.information(self, "Importation réussie", "Les utilisateurs ont été importés avec succès depuis le fichier CSV.") - self.update_user_table() - else: - # Enregistrement de l'erreur dans les logs - log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) - # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - - def export_to_csv(self): - current_tab_index = self.tab_widget.currentIndex() - - try: - if current_tab_index == 0: # Export books - file_path, _ = QFileDialog.getSaveFileName(self, "Enregistrer le fichier CSV", "", "CSV Files (*.csv)") - if file_path: - with open(file_path, 'w', newline='', encoding='utf-8') as file: - writer = csv.writer(file) - writer.writerow(['Title', 'Author', 'Publisher', 'ISBN', 'Total Copies']) - for book in self.library.display_books(): - writer.writerow([book.title, book.author, book.publisher, book.isbn, book.available_copies]) - log_action("Exportation réussie des livres vers un fichier CSV", success=True) - QMessageBox.information(self, "Exportation réussie", "Les données des livres ont été exportées avec succès vers un fichier CSV.") - - elif current_tab_index == 1: # Export users - file_path, _ = QFileDialog.getSaveFileName(self, "Enregistrer le fichier CSV", "", "CSV Files (*.csv)") - if file_path: - with open(file_path, 'w', newline='', encoding='utf-8') as file: - writer = csv.writer(file) - writer.writerow(['Fname', 'Name', 'Email', 'Address', 'Empreint']) - for user in self.user_gestion.display_users(): - writer.writerow([user.fname, user.name, user.mail, user.address, user.take]) - log_action("Exportation réussie des utilisateurs vers un fichier CSV", success=True) - QMessageBox.information(self, "Exportation réussie", "Les données des utilisateurs ont été exportées avec succès vers un fichier CSV.") - - except Exception as e: - log_action(f"Erreur lors de l'exportation vers un fichier CSV : {str(e)}", success=False) - QMessageBox.critical(self, "Erreur d'exportation", f"Une erreur est survenue lors de l'exportation : {str(e)}") - - - def export_save_to_csv(self, file_path, books): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index == 0: - # Exporter les données des livres dans un fichier CSV - try: - with open(file_path, 'w', newline='', encoding='utf-8') as file: - writer = csv.writer(file) - writer.writerow(['ID', 'Title', 'Author', 'Publisher', 'ISBN', 'Total Copies', 'Available Copies']) - - for book in books: - writer.writerow([ - book.book_id, - book.title, - book.author, - book.publisher, - book.isbn, - book.total_copies, - book.available_copies - ]) - - log_action(f"Sauvegarde réussie vers {file_path}", success=True) - except Exception as e: - log_action(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) - - - # Fonction d'importation depuis un fichier CSV save - def import_save_from_csv(self, file_path): - try: - success = self.library.import_from_csv(file_path) - if success: - # Enregistrement de l'action dans les logs - log_action("Importation réussie depuis un fichier CSV", success=True) - QMessageBox.information(self, "Importation réussie", "Les livres ont été importés avec succès depuis le fichier CSV.") - else: - # Enregistrement de l'erreur dans les logs - log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - except Exception as e: - # Enregistrement de l'erreur dans les logs - log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) - # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) - QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") - - - # Fonction pour éditer une cellule lors du double-clic - def edit_cell(self, item): - current_tab_index = self.tab_widget.currentIndex() - if current_tab_index==0: - current_column = self.book_table.currentColumn() - self.book_table.editItem(item, current_column) - - if current_tab_index==1: - current_column = self.user_table.currentColumn() - self.user_table.editItem(item, current_column) - - if current_tab_index==2: - current_column = self.take_table.currentColumn() - self.take_table.editItem(item, current_column) - - def rename_column(self, column): - new_name, ok = QInputDialog.getText(self, "Renommer la colonne", f"Entrez un nouveau nom pour la colonne {column + 1}") - if ok and new_name: - self.book_table.headerItem().setText(column, new_name) - - - def search_books(self): - query = self.entry_search.text() - search_type = self.search_combobox.currentText() - - if search_type == "ISBN": - books = self.library.display_books(query=query, by_isbn=True) - elif search_type == "Titre": - books = self.library.display_books(query=query, by_title=True) - elif search_type == "Auteur": - books = self.library.display_books(query=query, by_author=True) - elif search_type == "Exemplaires Disponible": - books = self.library.display_books(query=query, by_available_copies=True) - elif search_type == "Exemplaires": - books = self.library.display_books(query=query, by_total_copies=True) - elif search_type == "Maison d'édition": - books = self.library.display_books(query=query, by_publisher=True) - elif search_type == "ID": - books = self.library.display_books(query=query, by_ID=True) - - else: - books = self.library.display_books(query=query) - - self.update_book_table(books) - #"Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Diponibles", "ID" +from tools.app import * def main(): app = QApplication(sys.argv) diff --git a/tools/__pycache__/library_logic.cpython-311.pyc b/tools/__pycache__/library_logic.cpython-311.pyc new file mode 100644 index 0000000..a826474 Binary files /dev/null and b/tools/__pycache__/library_logic.cpython-311.pyc differ diff --git a/tools/__pycache__/logs.cpython-311.pyc b/tools/__pycache__/logs.cpython-311.pyc new file mode 100644 index 0000000..51fdc0d Binary files /dev/null and b/tools/__pycache__/logs.cpython-311.pyc differ diff --git a/tools/app.py b/tools/app.py new file mode 100644 index 0000000..9104ad8 --- /dev/null +++ b/tools/app.py @@ -0,0 +1,906 @@ + +from tools.logs import * +create_log_directory() +log_action(f"Importation du fichier 'logs.py'", success=True) + +import sys +log_action(f"Importation de la biblioteque 'sys'", success=True) +import csv +log_action(f"Importation de la biblioteque 'csv'", success=True) +import os +log_action(f"Importation de la biblioteque 'os'", success=True) + +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout, + QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget, + QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, + QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser) +log_action(f"Importation de la biblioteque 'PyQt5.QtWidgets' avec comme fonction 'QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout,QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget,QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser'", success=True) +from PyQt5.QtGui import (QPixmap, QIcon) +log_action(f"Importation de la biblioteque 'PyQt5.QtGui' avec comme fonction 'QPixmap, QIcon'", success=True) +from PyQt5.QtCore import Qt, QSettings +log_action(f"Importation de la biblioteque 'PyQt5.QtCore' avec comme fonction 'Qt, QSettings'", success=True) +from PyQt5.QtPrintSupport import QPrinter, QPrintDialog +log_action(f"Importation de la biblioteque 'PyQt5.QtPrintSupport' avec comme fonction 'QPrinter, QPrintDialog'", success=True) +from reportlab.pdfgen import canvas +log_action(f"Importation de la biblioteque 'reportlab.pdfgen' avec comme fonction 'canvas'", success=True) +from reportlab.lib.pagesizes import letter +log_action(f"Importation de la biblioteque 'reportlab.lib.pagesizes' avec comme fonction 'letter'", success=True) + +from tools.library_logic import * +log_action(f"Importation du fichier 'library_logic.py'", success=True) +from tools.user_logic import * +log_action(f"Importation du fichier 'user_logic.py'", success=True) +from tools.dialog_logic import * +log_action(f"Importation du fichier 'dialog_logic.py'", success=True) +from tools.menu_setup import create_menu +log_action(f"Importation du fichier 'menu_setup.py'", success=True) + +def load_stylesheet(file_path): + with open(file_path, 'r') as file: + return file.read() + +class LibraryApp(QMainWindow): + def __init__(self): + super().__init__() + + # Set window properties + self.setWindowTitle("Jobi - Gestionnaire de bibliothèque") + self.setGeometry(100, 100, 800, 500) + self.setWindowIcon(QIcon('icon.png')) + + # Create a central widget to hold the layout + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + + # Apply CSS styles to certain components + self.setStyleSheet(load_stylesheet('content\\css\\style.css')) + + # Create a main layout for the central widget + self.layout = QVBoxLayout(self.central_widget) + + # Determine the current directory + current_dir = os.path.dirname(os.path.realpath(__file__)) + + self.file_path = None + self.file_path_book = None + self.file_path_user = None + self.file_path_take = None + + # Display the logo in the banner + self.logo_label = QLabel(self) + self.pixmap = QPixmap(os.path.join(current_dir, 'icon.png')) + self.logo_label.setPixmap(self.pixmap) + + self.settings = QSettings("Jobi", "gestion_library_app") + + self.library = Library() + self.user_gestion = User_gestion() + + # Create a tab widget without specifying geometry + self.tab_widget = QTabWidget(self.central_widget) + self.tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + # Books Tab + self.books_tab = QWidget() + books_layout = QVBoxLayout() + self.setup_search_section() + self.setup_table("book_table",7,["ID", "Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Disponibles"]) + self.book_table.itemChanged.connect(self.update_book_info) + books_layout.addWidget(self.book_table) + self.books_tab.setLayout(books_layout) + self.tab_widget.addTab(self.books_tab, "Gestion des Livres") + + # Users Tab + self.users_tab = QWidget() + user_layout = QVBoxLayout() + self.setup_user_tab() + self.user_table.itemChanged.connect(self.update_user_info) + self.users_tab.setLayout(user_layout) + self.tab_widget.addTab(self.users_tab, "Gestion des Utilisateurs") + + # Take Tab + self.take_tab = QWidget() + take_layout = QVBoxLayout() + self.setup_take_tab() + self.take_tab.setLayout(take_layout) + self.tab_widget.addTab(self.take_tab, "Gestion des Empreints") + + # Set up stretch factors to prioritize the content in the layout + self.layout.addWidget(self.tab_widget, stretch=1) + + # Load saved CSV data + self.load_saved_csv() + + # Add the bottom layout to the central layout + self.layout.addLayout(self.setup_bottom_layout()) + + # Initialize bottom_layout as an instance attribute + self.bottom_layout = None + + self.tab_widget.currentChanged.connect(self.onTabChanged) + + create_menu(self) + + #Zone de text pour n'import quelle fonctionnalisté +# self.text_browser = QTextBrowser(self) +# books_layout.addWidget(self.text_browser) + + def supprimer_logs(self): + + dossier= "data/logs/" + + # Vérifier si le dossier existe + if not os.path.exists(dossier): + log_action(f"Le dossier '{dossier}' n'existe pas.", False, "Dossier introuvable") + QMessageBox.warning(self, "Dossier introuvable","Le dossier '{dossier}' n'existe pas.") + return + + # Liste tous les fichiers dans le dossier + fichiers = os.listdir(dossier) + + # Parcourir tous les fichiers + for fichier in fichiers: + # Vérifier si le fichier est un fichier .log + if fichier.endswith(".log"): + chemin_fichier = os.path.join(dossier, fichier) + # Supprimer le fichier + os.remove(chemin_fichier) + log_action(f"Fichier '{fichier}' supprimé avec succès.", True) + QMessageBox.information(self, "Tout les fichiers ont été supprimé avec succès", "Le cache à bien été vidé") + + def toggle_fullscreen(self): + if self.isFullScreen(): + self.showNormal() + else: + self.showFullScreen() + + def print_all_files(self): + QMessageBox.information(self, "En développement", "Cette fonctionnalité est en cours de développement") +# if self.tab_widget.currentIndex()==0: +# # Imprimer tous les livres +# self.print_files(self.library.books) + + def print_selected_file(self): + if self.tab_widget.currentIndex()==0: + # Demander à l'utilisateur d'entrer l'ID du livre + book_id, ok = QInputDialog.getText(self, 'Entrer l\'ID du livre', 'ID du livre:') + if ok: + if book_id: + book = self.library.get_book_by_id(book_id) + if book: + self.print_files([book]) + else: + self.text_browser.setText(f"Aucun livre trouvé avec l'ID {book_id}") + else: + self.text_browser.setText("Veuillez spécifier un ID de livre") + + def print_files(self, books): + if self.tab_widget.currentIndex() == 0: + # Demander à l'utilisateur où enregistrer le fichier PDF + file_path, _ = QFileDialog.getSaveFileName(self, 'Enregistrer le PDF', '', 'PDF Files (*.pdf)') + if not file_path: + return # L'utilisateur a annulé la sauvegarde + + try: + # Créer un fichier PDF avec la bibliothèque reportlab + pdf = canvas.Canvas(file_path, pagesize=letter) + pdf.setFont("Helvetica", 12) + + # Imprimer les détails des livres dans le PDF + for book in books: + pdf.drawString(100, 750, f"Book ID: {book.book_id}") + pdf.drawString(100, 730, f"Title: {book.title}") + pdf.drawString(100, 710, f"Author: {book.author}") + pdf.drawString(100, 690, f"ISBN: {book.isbn}") + pdf.drawString(100, 670, f"Total Copies: {book.total_copies}") + pdf.drawString(100, 650, f"Available Copies: {book.available_copies}") + pdf.drawString(100, 630, f"Publisher: {book.publisher}") + pdf.drawString(100, 610, "\n") + + # Sauvegarder et fermer le fichier PDF + pdf.save() + + # Afficher une boîte de dialogue pour indiquer que le PDF a été créé avec succès + QMessageBox.information(self, "PDF Créé", "Le PDF a été créé avec succès.", QMessageBox.Ok) + + # Journaliser l'action + log_action(f"Création du fichier PDF réussie : {file_path}", success=True) + except Exception as e: + # Journaliser l'erreur en cas d'échec + log_action(f"Erreur lors de la création du fichier PDF : {str(e)}", success=False) + + + def onTabChanged(self, index): + if index == 0: + new_text = 'Ajouter un livre' + self.btn_open_add_book_dialog.setText(new_text) + log_action('Changement d\'onglet vers \'Ajouter un livre\'', success=True) + self.setWindowTitle(f"Jobi - {self.file_path_book if self.file_path_book!=None else 'Gestionnaire de bibliothèque'}") + log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_book if self.file_path_book!=None else 'Gestionnaire de bibliothèque'}'") + elif index == 1: + new_text = 'Ajouter un utilisateur' + self.btn_open_add_book_dialog.setText(new_text) + log_action('Changement d\'onglet vers \'Ajouter un utilisateur\'', success=True) + self.setWindowTitle(f"Jobi - {self.file_path_user if self.file_path_user!=None else 'Gestionnaire de bibliothèque'}") + log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_user if self.file_path_user!=None else 'Gestionnaire de bibliothèque'}'") + elif index == 2: + new_text = 'Ajouter un emprunt' + self.btn_open_add_book_dialog.setText(new_text) + log_action('Changement d\'onglet vers \'Ajouter un emprunt\'', success=True) + self.setWindowTitle(f"Jobi - {self.file_path_take if self.file_path_take!=None else 'Gestionnaire de bibliothèque'}") + log_action(f"Changement du Windows Title par 'Jobi - {self.file_path_take if self.file_path_take!=None else 'Gestionnaire de bibliothèque'}'") + + def closeEvent(self, event): + # Sauvegarde du fichier CSV avant de fermer l'application + log_action("Fermeture de l'application. Tentative de sauvegarde...") + try: + save_directory = os.path.join("data", "save") + os.makedirs(save_directory, exist_ok=True) # Création du répertoire de sauvegarde s'il n'existe pas + save_path = os.path.join(save_directory, "saved_books.csv") + self.export_save_to_csv(save_path, librairy.books) # Utilisation de la fonction d'exportation existante + except Exception as e: + log_action(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) + print(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) + + def load_saved_csv(self): + saved_file_path = os.path.join("data", "save", "saved_books.csv") + if os.path.exists(saved_file_path): + self.file_path = saved_file_path + books = self.library.import_from_csv(saved_file_path) + print(books) # Ajoutez ce print pour vérifier les données chargées depuis le CSV + self.update_book_table() + else: + log_action("Aucun fichier sauvegardé trouvé.", False) + + + def open_add_book_dialog(self): + dialog = AddBookDialog(self) + dialog.library_app = self + dialog.exec_() + + + def setup_search_section(self): + search_layout = QHBoxLayout() + + self.search_label = QLabel("Recherche:") + self.entry_search = QLineEdit() + self.entry_search.setObjectName("entry_search") + + self.entry_search.textChanged.connect(self.search_books) + search_layout.addWidget(self.search_label) + search_layout.addWidget(self.entry_search) + + self.search_type_label = QLabel("Type :") + self.search_combobox = QComboBox() + self.search_combobox.setObjectName("search_combobox") + + search_options = ["Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Diponibles", "ID"] + self.search_combobox.addItems(search_options) + self.search_combobox.currentIndexChanged.connect(self.search_books) + search_layout.addWidget(self.search_type_label) + search_layout.addWidget(self.search_combobox) + + self.layout.addLayout(search_layout) + + def setup_table(self, val_name, colomn_nbr, colomn_names): + # Utilisez setattr pour créer dynamiquement un attribut d'instance avec le nom spécifié + setattr(self, val_name, QTreeWidget()) + + # Utilisez l'attribut d'instance pour accéder à votre QTreeWidget + getattr(self, val_name).setColumnCount(colomn_nbr) + getattr(self, val_name).setHeaderLabels(colomn_names) +# getattr(self, val_name).setSortingEnabled(True) # A resoudre pour eviter le mauvais rangement + + header = getattr(self, val_name).header() + header.setSectionResizeMode(QHeaderView.ResizeToContents) + + # Ajoutez le QTreeWidget au layout de votre classe + self.layout.addWidget(getattr(self, val_name)) + + # Rendre tous les éléments éditables dans le tableau + getattr(self, val_name).setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked | QAbstractItemView.EditKeyPressed) + + # Activer la détection du clic droit pour le menu contextuel + getattr(self, val_name).setContextMenuPolicy(Qt.CustomContextMenu) + getattr(self, val_name).customContextMenuRequested.connect(self.show_user_context_menu) + + + def setup_user_tab(self): + # Set up the user management components in the users tab + user_layout = QVBoxLayout() + + # Set up the user management table and related components in the users tab + self.setup_table("user_table", 6,["ID", "Nom", "Prénom", "Email", "Addresse", "Nombre d'Empreint"]) + self.user_table.itemChanged.connect(self.update_user_info) + + user_layout.addWidget(self.user_table) + + self.users_tab.setLayout(user_layout) + + def show_user_context_menu(self, pos): + menu = QMenu(self) + copy_action = menu.addAction("Copier") + delete_action = menu.addAction("Supprimer") + modify_action = menu.addAction("Modifier") + + action = menu.exec_(self.user_table.mapToGlobal(pos)) + if action == copy_action: + selected_item = self.user_table.currentItem() + if selected_item is not None: + clipboard = QApplication.clipboard() + clipboard.setText(selected_item.text(self.user_table.currentColumn())) + elif action == modify_action: + selected_item = self.user_table.currentItem() + if selected_item is not None: + self.edit_cell(selected_item) + elif action == delete_action: + selected_item = self.user_table.currentItem() + if selected_item is not None: + log_action(f"Tentative de suppression de l'utilisateur avec l'ID={selected_item.text(0)}", success=True) + # Handle deletion confirmation and process similarly to books if needed + + def update_user_info(self): + try: + selected_items = self.user_table.selectedItems() + for item in selected_items: + user_id = int(item.text(0)) + user = self.user_gestion.get_user_by_id(user_id) + + column = self.user_table.currentColumn() + column_name = self.user_table.headerItem().text(column) # Récupération du nom de la colonne + + # Mettre à jour les détails de l'utilisateur selon la colonne + # Example: if column == 1: user.name = new_value + # ... + + # Appel à la méthode de la classe Library pour mettre à jour les détails de l'utilisateur + # self.library.update_user_details(user) + # ... + + # Log action pour indiquer la modification de la colonne + log_action(f"Modification sur la colonne '{column_name}': 'old_value' -> 'new_value'", success=True) + + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de la mise à jour des détails de l'utilisateur : {str(e)}", success=False) + print("Une erreur s'est produite :", e) + + def setup_take_table(self): + self.take_table = QTreeWidget() + self.take_table.setColumnCount(3) # Adjust the number of columns as needed + self.take_table.setHeaderLabels(["ID", "ID du livre", "Titre", "Auteur", "ISBN", "ID de l'empreinteur", "Nom de l'empreinteur", "Prénom de l'empreinteur"]) + header = self.take_table.header() + header.setSectionResizeMode(QHeaderView.ResizeToContents) + self.layout.addWidget(self.take_table) + + # Rendre tous les éléments éditables dans le tableau + self.take_table.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked | QAbstractItemView.EditKeyPressed) + + # Activer la détection du clic droit pour le menu contextuel + self.take_table.setContextMenuPolicy(Qt.CustomContextMenu) + self.take_table.customContextMenuRequested.connect(self.show_take_context_menu) + + def setup_take_tab(self): + # Set up the user management components in the users tab + take_layout = QVBoxLayout() + + # Set up the user management table and related components in the users tab + self.setup_take_table() + self.take_table.itemChanged.connect(self.update_take_info) + + take_layout.addWidget(self.take_table) + + self.take_tab.setLayout(take_layout) + + def show_take_context_menu(self, pos): + menu = QMenu(self) + copy_action = menu.addAction("Copier") + delete_action = menu.addAction("Supprimer") + modify_action = menu.addAction("Modifier") + + action = menu.exec_(self.take_table.mapToGlobal(pos)) + if action == copy_action: + selected_item = self.take_table.currentItem() + if selected_item is not None: + clipboard = QApplication.clipboard() + clipboard.setText(selected_item.text(self.take_table.currentColumn())) + elif action == modify_action: + selected_item = self.take_table.currentItem() + if selected_item is not None: + self.edit_take_cell(selected_item) + elif action == delete_action: + selected_item = self.take_table.currentItem() + if selected_item is not None: + log_action(f"Tentative de suppression de l'empreint avec l'ID={selected_item.text(0)}", success=True) + # Handle deletion confirmation and process similarly to books if needed + + def update_take_info(self): + try: + selected_items = self.take_table.selectedItems() + for item in selected_items: + take_id = int(item.text(0)) + take = self.library.get_take_by_id(take_id) + + column = self.user_table.currentColumn() + column_name = self.user_table.headerItem().text(column) # Récupération du nom de la colonne + + # Mettre à jour les détails de l'utilisateur selon la colonne + # Example: if column == 1: take.name = new_value + # ... + + # Appel à la méthode de la classe Library pour mettre à jour les détails de l'empreint + # self.library.update_take_details(take) + # ... + + # Log action pour indiquer la modification de la colonne + log_action(f"Modification sur la colonne '{column_name}': 'old_value' -> 'new_value'", success=True) + + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de la mise à jour des détails de l'empreint : {str(e)}", success=False) + print("Une erreur s'est produite :", e) + + def setup_borrow_return_remove_sections(self): + # Add the borrow/return/remove sections directly to the main layout + self.setup_borrow_return_remove_buttons() + self.setup_import_export_buttons() + + def setup_borrow_return_remove_buttons(self): + borrow_return_layout = QHBoxLayout() + + self.borrow_label = QLabel("ID à emprunter:") + self.return_label = QLabel("ID à retourner:") + self.btn_take = QPushButton("Emprunter") + self.btn_return = QPushButton("Retourner") + + self.entry_take = QLineEdit() + self.entry_return = QLineEdit() + + self.btn_take.clicked.connect(self.take_book) + + borrow_return_layout.addWidget(self.borrow_label) + borrow_return_layout.addWidget(self.entry_take) + + borrow_return_layout.addWidget(self.btn_take) + + borrow_return_layout.addWidget(self.return_label) + borrow_return_layout.addWidget(self.entry_return) + + + self.btn_return.clicked.connect(self.return_book) + borrow_return_layout.addWidget(self.btn_return) + + self.remove_label = QLabel("ID à supprimer:") + self.entry_remove = QLineEdit() + borrow_return_layout.addWidget(self.remove_label) + borrow_return_layout.addWidget(self.entry_remove) + + self.btn_remove = QPushButton("Supprimer livre") + self.btn_remove.clicked.connect(self.remove_book) + borrow_return_layout.addWidget(self.btn_remove) + + # Add the borrow/return/remove layout to the main layout + self.layout.addLayout(borrow_return_layout) + + def setup_bottom_layout(self): + # Create a layout for the buttons at the bottom + self.bottom_layout = QHBoxLayout() + + # Add the borrow, return, and remove buttons to the bottom layout + self.setup_borrow_return_remove_buttons() + + # Add the import and export buttons to the bottom layout + self.setup_import_export_buttons() + + # Add stretch factor to push buttons to the right + self.bottom_layout.addStretch(1) + + return self.bottom_layout + + def setup_import_export_buttons(self): + self.import_export_layout = QHBoxLayout() + + self.btn_import = QPushButton("Importer depuis CSV") + self.btn_import.clicked.connect(self.import_from_csv) + self.import_export_layout.addWidget(self.btn_import) + +# self.current = QPushButton("onglet") +# self.current.clicked.connect(self.currentonglet) +# self.import_export_layout.addWidget(self.current) + + self.btn_export = QPushButton("Exporter vers CSV") + self.btn_export.clicked.connect(self.export_to_csv) + self.import_export_layout.addWidget(self.btn_export) + + self.btn_open_add_book_dialog = QPushButton("Ajouter un livre") + self.btn_open_add_book_dialog.clicked.connect(self.open_add_book_dialog) + self.import_export_layout.addWidget(self.btn_open_add_book_dialog) + + self.layout.addLayout(self.import_export_layout) + self.layout.update() + + # Ajoute cette méthode dans AddBookDialog + def set_library_app_reference(self, library_app): + self.library_app = library_app + +# def currentonglet(self): +# print(self.tab_widget.currentIndex()) + + # Fonction pour afficher le menu contextuel lors du clic droit + def show_context_menu(self, pos): + menu = QMenu(self) + copy_action = menu.addAction("Copier") + delete_action = menu.addAction("Supprimer") + modify_action = menu.addAction("Modifier") + + action = menu.exec_(self.book_table.mapToGlobal(pos)) + if action == copy_action: + selected_item = self.book_table.currentItem() + if selected_item is not None: + clipboard = QApplication.clipboard() + clipboard.setText(selected_item.text(self.book_table.currentColumn())) + elif action == modify_action: + selected_item = self.book_table.currentItem() + if selected_item is not None: + self.edit_cell(selected_item) + elif action == delete_action: + selected_item = self.book_table.currentItem() + if selected_item is not None: + log_action(f"Tentative de suppression de l'élément avec l'ID={selected_item.text(0)}", success=True) + # Vérifie si la case à cocher "Ne plus afficher" est déjà cochée + never_show_checked = self.settings.value("NeverShowConfirmation", False, type=bool) + if not never_show_checked: + confirmation_dialog = ConfirmationDialog("Êtes-vous sûr de vouloir supprimer cet élément ?") + result = confirmation_dialog.exec_() + if result == QDialog.Accepted: + should_hide_dialog = confirmation_dialog.never_show_checkbox.isChecked() + if should_hide_dialog: + self.settings.setValue("NeverShowConfirmation", True) # Enregistrer le choix utilisateur + self.delete_selected_item() # Appel à la fonction de suppression + confirmation_dialog.deleteLater() # Supprimer la boîte de dialogue après utilisation + else: + self.delete_selected_item() # Si "Ne plus afficher" est déjà coché, supprime directement l'élément + + + def update_book_info(self): + try: + selected_items = self.book_table.selectedItems() + for item in selected_items: + book_id = int(item.text(0)) + book = self.library.get_book_by_id(book_id) + + column = self.book_table.currentColumn() + column_name = self.book_table.headerItem().text(column) # Récupération du nom de la colonne + + # Mettre à jour les détails du livre selon la colonne + if column == 1: # Supposez que la colonne 1 correspond au titre du livre + old_value = book.title + new_value = item.text(column) + book.title = new_value + elif column == 2: # Colonne pour l'auteur + old_value = book.author + new_value = item.text(column) + book.author = new_value + elif column == 3: # Colonne pour la maison d'édition + old_value = book.publisher + new_value = item.text(column) + book.publisher = new_value + elif column == 4: # Colonne pour l'ISBN + old_value = book.isbn + new_value = item.text(column) + book.isbn = new_value + elif column == 5: # Colonne pour le nombre total d'exemplaires + old_value = book.total_copies + new_value = int(item.text(column)) + book.total_copies = new_value + elif column == 6: # Colonne pour le nombre d'exemplaires disponibles + old_value = book.available_copies + new_value = int(item.text(column)) + book.available_copies = new_value + # Mise à jour du nombre d'exemplaires disponibles si nécessaire + # book.available_copies = ... (calcul pour déterminer les exemplaires disponibles) + + # Appel à la méthode de la classe Library pour mettre à jour les détails du livre + self.library.update_book_details(book) + self.update_book_table() + + # Log action pour indiquer la modification de la colonne + log_action(f"Modification sur la colonne '{column_name}': '{old_value}' -> '{new_value}'", success=True) + + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de la mise à jour des détails du livre : {str(e)}", success=False) + print("Une erreur s'est produite :", e) + + + def update_book_table(self, books=None): + self.update_table(self.book_table, books, ['book_id', 'title', 'author', 'publisher', 'isbn', 'total_copies', 'available_copies']) + + def update_user_table(self, users=None): + self.update_table(self.user_table, users, ['user_id', 'fname', 'name', 'mail', 'take']) + + def update_table(self, table_widget, items=None, column_names=None): + """ + Met à jour un QTreeWidget avec une liste d'items en fonction des noms de colonnes spécifiés. + + :param table_widget: QTreeWidget à mettre à jour + :param items: Liste des éléments à afficher dans le tableau + :param column_names: Liste des noms de colonnes pour l'affichage + """ + # Efface le contenu actuel du tableau + table_widget.clear() + + # Détermine les éléments à afficher si non fournis + if not items: + if 'book_id' in column_names: + items = self.library.display_books() + elif 'user_id' in column_names: + items = self.user_gestion.display_users() + + # Mise à jour du tableau en fonction des colonnes + for item in items: + tree_item = QTreeWidgetItem(table_widget) + + # Associe les données aux colonnes de manière récursive + for col, column_name in enumerate(column_names): + # Utilise getattr pour obtenir la valeur de l'attribut + value = getattr(item, column_name, '') + # Gère les cas où la valeur peut être de type non chaîne (par exemple, un nombre) + tree_item.setText(col, str(value)) + + # Rendre tous les éléments de cet item éditables + for column in range(table_widget.columnCount()): + tree_item.setFlags(tree_item.flags() | Qt.ItemIsEditable) + + def take_book(self): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + book_id = self.entry_take.text() + if book_id=='': + log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") + QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") + + else: + success, message = self.library.take_book_by_id(book_id) + if success: + # Enregistrement de l'action dans les logs + log_action(f"Emprunt d'un livre avec l'ID={book_id}", success=True) + QMessageBox.information(self, "Emprunt", message) + self.update_book_table() + else: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de l'emprunt d'un livre avec l'ID={book_id}: {str(message)}", success=False) + QMessageBox.warning(self, "Emprunt impossible", message) + + def return_book(self): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + book_id = self.entry_return.text() + if book_id=='': + log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") + QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") + + else: + success, message = self.library.return_book_by_id(book_id) + if success: + # Enregistrement de l'action dans les logs + log_action(f"Retour d'un livre avec l'ID={book_id}", success=True) + QMessageBox.information(self, "Retour", message) + self.update_book_table() + else: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors du retour d'un livre avec l'ID={book_id}: {str(message)}", success=False) + QMessageBox.warning(self, "Retour impossible", message) + + def remove_book(self): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + book_id = self.entry_remove.text() + if book_id=='': + log_action("Aucune ID de livre n'a été défini !", success=False, error_message="ID non défini") + QMessageBox.warning(self, "ID non défini", "Aucune ID de livre n'a été défini !") + + else: + success, message = self.library.remove_book_by_id(book_id) + if success: + # Enregistrement de l'action dans les logs + log_action(f"Suppression d'un livre avec l'ID={book_id}", success=True) + QMessageBox.information(self, "Suppression", message) + self.update_book_table() + else: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de la suppression d'un livre avec l'ID={book_id}: {str(message)}", success=False) + QMessageBox.warning(self, "Suppression impossible", message) + + def delete_selected_item(self): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + selected_item = self.book_table.currentItem() + if selected_item is not None: + book_id = selected_item.text(0) + # Utilisez la fonction remove_book_by_id de la bibliothèque pour supprimer l'élément + success, message = self.library.remove_book_by_id(book_id) + if success: + # Enregistrement de l'action dans les logs + log_action(f"Suppression d'un livre avec l'ID={book_id}", success=True) + QMessageBox.information(self, "Suppression", message) + self.update_book_table() # Mettre à jour l'interface utilisateur après la suppression + else: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de la suppression d'un livre avec l'ID={book_id}: {message}", success=False) + QMessageBox.warning(self, "Suppression impossible", message) + + def import_from_csv(self): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + new_book_id = None # Initialisation de new_book_id en dehors du bloc try + + try: + file_path, _ = QFileDialog.getOpenFileName(self, "Sélectionner un fichier CSV", "", "CSV Files (*.csv)") + self.setWindowTitle(f"Jobi - {file_path}") + log_action(f"Changement du Windows Title par 'Jobi - {file_path}'") + self.file_path_book = file_path + log_action(f"Modification de la banniere superieur par : {file_path}", success=False) + if file_path: + success = self.library.import_from_csv(file_path) + if success: + # Enregistrement de l'action dans les logs + log_action("Importation réussie depuis un fichier CSV", success=True) + QMessageBox.information(self, "Importation réussie", "Les livres ont été importés avec succès depuis le fichier CSV.") + self.update_book_table() + else: + # Enregistrement de l'erreur dans les logs + log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) + # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + + if current_tab_index == 1: + new_user_id = None # Initialisation de new_user_id en dehors du bloc try + + try: + file_path, _ = QFileDialog.getOpenFileName(self, "Sélectionner un fichier CSV", "", "CSV Files (*.csv)") + self.setWindowTitle(f"Jobi - {file_path}") + self.file_path_user = file_path + log_action(f"Modification de la banniere superieur par : {file_path}", success=False) + if file_path: + success = self.user_gestion.import_from_csv(file_path) + if success: + # Enregistrement de l'action dans les logs + log_action("Importation réussie depuis un fichier CSV", success=True) + QMessageBox.information(self, "Importation réussie", "Les utilisateurs ont été importés avec succès depuis le fichier CSV.") + self.update_user_table() + else: + # Enregistrement de l'erreur dans les logs + log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) + # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + + def export_to_csv(self): + current_tab_index = self.tab_widget.currentIndex() + + try: + if current_tab_index == 0: # Export books + file_path, _ = QFileDialog.getSaveFileName(self, "Enregistrer le fichier CSV", "", "CSV Files (*.csv)") + if file_path: + with open(file_path, 'w', newline='', encoding='utf-8') as file: + writer = csv.writer(file) + writer.writerow(['Title', 'Author', 'Publisher', 'ISBN', 'Total Copies']) + for book in self.library.display_books(): + writer.writerow([book.title, book.author, book.publisher, book.isbn, book.available_copies]) + log_action("Exportation réussie des livres vers un fichier CSV", success=True) + QMessageBox.information(self, "Exportation réussie", "Les données des livres ont été exportées avec succès vers un fichier CSV.") + + elif current_tab_index == 1: # Export users + file_path, _ = QFileDialog.getSaveFileName(self, "Enregistrer le fichier CSV", "", "CSV Files (*.csv)") + if file_path: + with open(file_path, 'w', newline='', encoding='utf-8') as file: + writer = csv.writer(file) + writer.writerow(['Fname', 'Name', 'Email', 'Address', 'Empreint']) + for user in self.user_gestion.display_users(): + writer.writerow([user.fname, user.name, user.mail, user.address, user.take]) + log_action("Exportation réussie des utilisateurs vers un fichier CSV", success=True) + QMessageBox.information(self, "Exportation réussie", "Les données des utilisateurs ont été exportées avec succès vers un fichier CSV.") + + except Exception as e: + log_action(f"Erreur lors de l'exportation vers un fichier CSV : {str(e)}", success=False) + QMessageBox.critical(self, "Erreur d'exportation", f"Une erreur est survenue lors de l'exportation : {str(e)}") + + + def export_save_to_csv(self, file_path, books): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index == 0: + # Exporter les données des livres dans un fichier CSV + try: + with open(file_path, 'w', newline='', encoding='utf-8') as file: + writer = csv.writer(file) + writer.writerow(['ID', 'Title', 'Author', 'Publisher', 'ISBN', 'Total Copies', 'Available Copies']) + + for book in books: + writer.writerow([ + book.book_id, + book.title, + book.author, + book.publisher, + book.isbn, + book.total_copies, + book.available_copies + ]) + + log_action(f"Sauvegarde réussie vers {file_path}", success=True) + except Exception as e: + log_action(f"Erreur lors de la sauvegarde du fichier : {str(e)}", False, str(e)) + + + # Fonction d'importation depuis un fichier CSV save + def import_save_from_csv(self, file_path): + try: + success = self.library.import_from_csv(file_path) + if success: + # Enregistrement de l'action dans les logs + log_action("Importation réussie depuis un fichier CSV", success=True) + QMessageBox.information(self, "Importation réussie", "Les livres ont été importés avec succès depuis le fichier CSV.") + else: + # Enregistrement de l'erreur dans les logs + log_action("Erreur lors de l'importation depuis un fichier CSV", success=False) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + except Exception as e: + # Enregistrement de l'erreur dans les logs + log_action(f"Erreur lors de l'importation depuis un fichier CSV : {str(e)}", success=False) + # Gérer l'erreur (affichage d'un message à l'utilisateur ou autre) + QMessageBox.critical(self, "Erreur d'importation", "Une erreur est survenue lors de l'importation depuis le fichier CSV.") + + + # Fonction pour éditer une cellule lors du double-clic + def edit_cell(self, item): + current_tab_index = self.tab_widget.currentIndex() + if current_tab_index==0: + current_column = self.book_table.currentColumn() + self.book_table.editItem(item, current_column) + + if current_tab_index==1: + current_column = self.user_table.currentColumn() + self.user_table.editItem(item, current_column) + + if current_tab_index==2: + current_column = self.take_table.currentColumn() + self.take_table.editItem(item, current_column) + + def rename_column(self, column): + new_name, ok = QInputDialog.getText(self, "Renommer la colonne", f"Entrez un nouveau nom pour la colonne {column + 1}") + if ok and new_name: + self.book_table.headerItem().setText(column, new_name) + + + def search_books(self): + query = self.entry_search.text() + search_type = self.search_combobox.currentText() + + if search_type == "ISBN": + books = self.library.display_books(query=query, by_isbn=True) + elif search_type == "Titre": + books = self.library.display_books(query=query, by_title=True) + elif search_type == "Auteur": + books = self.library.display_books(query=query, by_author=True) + elif search_type == "Exemplaires Disponible": + books = self.library.display_books(query=query, by_available_copies=True) + elif search_type == "Exemplaires": + books = self.library.display_books(query=query, by_total_copies=True) + elif search_type == "Maison d'édition": + books = self.library.display_books(query=query, by_publisher=True) + elif search_type == "ID": + books = self.library.display_books(query=query, by_ID=True) + + else: + books = self.library.display_books(query=query) + + self.update_book_table(books) + #"Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Diponibles", "ID" \ No newline at end of file diff --git a/tools/beta.py b/tools/beta.py new file mode 100644 index 0000000..a66684c --- /dev/null +++ b/tools/beta.py @@ -0,0 +1,290 @@ +# main.py + +from logs import * +create_log_directory() +log_action(f"Importation du fichier 'logs.py'", success=True) +import sys +log_action(f"Importation de la biblioteque 'sys'", success=True) +import csv +log_action(f"Importation de la biblioteque 'csv'", success=True) +import os +log_action(f"Importation de la biblioteque 'os'", success=True) +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout, + QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget, + QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, + QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser, + QTableWidget) +log_action(f"Importation de la biblioteque 'PyQt5.QtWidgets' avec comme fonction 'QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout,QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget,QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, QMenu, QCheckBox, QInputDialog, QTabWidget, QSizePolicy, QAction, QTextBrowser,QTableWidget'", success=True) +from PyQt5.QtGui import (QPixmap, QIcon) +log_action(f"Importation de la biblioteque 'PyQt5.QtGui' avec comme fonction 'QPixmap, QIcon'", success=True) +from PyQt5.QtCore import Qt, QSettings +log_action(f"Importation de la biblioteque 'PyQt5.QtCore' avec comme fonction 'Qt, QSettings'", success=True) +from PyQt5.QtPrintSupport import QPrinter, QPrintDialog +log_action(f"Importation de la biblioteque 'PyQt5.QtPrintSupport' avec comme fonction 'QPrinter, QPrintDialog'", success=True) +from reportlab.pdfgen import canvas +log_action(f"Importation de la biblioteque 'reportlab.pdfgen' avec comme fonction 'canvas'", success=True) +from reportlab.lib.pagesizes import letter +log_action(f"Importation de la biblioteque 'reportlab.lib.pagesizes' avec comme fonction 'letter'", success=True) +from library_logic import * +log_action(f"Importation du fichier 'library_logic.py'", success=True) +from user_logic import * +log_action(f"Importation du fichier 'user_logic.py'", success=True) +from dialog_logic import * +log_action(f"Importation du fichier 'dialog_logic.py'", success=True) + + +class LibraryApp(QMainWindow): + def __init__(self): + super(LibraryApp, self).__init__() + self.library = Library() + self.user_gestion = User_gestion() + + self.setup_ui() + + def setup_ui(self): + self.setWindowTitle("Application Bibliothèque") + self.setGeometry(100, 100, 800, 600) + self.setup_tabs() + self.setup_menus() + self.setup_layout() + self.setup_bottom_layout() + self.update_tables() + + def setup_tabs(self): + self.tab_widget = QTabWidget() + self.book_tab = QWidget() + self.user_tab = QWidget() + self.tab_widget.addTab(self.book_tab, "Livres") + self.tab_widget.addTab(self.user_tab, "Utilisateurs") + self.setup_book_tab() + self.setup_user_tab() + + def setup_book_tab(self): + self.setup_book_table() + self.setup_borrow_return_remove_buttons() + + def setup_user_tab(self): + self.setup_user_table() + + def setup_book_table(self): + self.book_table = QTableWidget() + self.book_table.doubleClicked.connect(self.edit_cell) + + def setup_user_table(self): + self.user_table = QTableWidget() + self.user_table.doubleClicked.connect(self.edit_cell) + + def setup_menus(self): + self.setup_file_menu() + self.setup_edit_menu() + + def setup_layout(self): + self.layout = QVBoxLayout() + self.layout.addWidget(self.tab_widget) + self.layout.addLayout(self.setup_bottom_layout()) + central_widget = QWidget() + central_widget.setLayout(self.layout) + self.setCentralWidget(central_widget) + + def setup_bottom_layout(self): + self.bottom_layout = QHBoxLayout() + self.setup_borrow_return_remove_buttons() + self.setup_import_export_buttons() + self.bottom_layout.addStretch(1) + return self.bottom_layout + + def setup_borrow_return_remove_buttons(self): + borrow_return_layout = QHBoxLayout() + self.borrow_label, self.return_label, self.remove_label = QLabel("ID à emprunter:"), QLabel( + "ID à retourner:"), QLabel("ID à supprimer:") + self.entry_take, self.entry_return, self.entry_remove = QLineEdit(), QLineEdit(), QLineEdit() + self.btn_take, self.btn_return, self.btn_remove = QPushButton("Emprunter"), QPushButton("Retourner"), QPushButton( + "Supprimer livre") + + self.btn_take.clicked.connect(self.take_book) + self.btn_return.clicked.connect(self.return_book) + self.btn_remove.clicked.connect(self.remove_book) + + borrow_return_layout.addWidget(self.borrow_label) + borrow_return_layout.addWidget(self.entry_take) + borrow_return_layout.addWidget(self.btn_take) + borrow_return_layout.addWidget(self.return_label) + borrow_return_layout.addWidget(self.entry_return) + borrow_return_layout.addWidget(self.btn_return) + borrow_return_layout.addWidget(self.remove_label) + borrow_return_layout.addWidget(self.entry_remove) + borrow_return_layout.addWidget(self.btn_remove) + + self.layout.addLayout(borrow_return_layout) + + def setup_import_export_buttons(self): + self.import_export_layout = QHBoxLayout() + self.btn_import, self.btn_export, self.btn_open_add_book_dialog = QPushButton( + "Importer depuis CSV"), QPushButton("Exporter vers CSV"), QPushButton("Ajouter un livre") + self.btn_import.clicked.connect(self.import_from_csv) + self.btn_export.clicked.connect(self.export_to_csv) + self.btn_open_add_book_dialog.clicked.connect(self.open_add_book_dialog) + self.import_export_layout.addWidget(self.btn_import) + self.import_export_layout.addWidget(self.btn_export) + self.import_export_layout.addWidget(self.btn_open_add_book_dialog) + self.layout.addLayout(self.import_export_layout) + + def set_library_app_reference(self, library_app): + self.library_app = library_app + + def show_context_menu(self, pos): + menu = QMenu(self) + actions = {"Copier": self.copy_selected_item, "Supprimer": self.delete_selected_item, "Modifier": self.edit_selected_item} + for action_text, action_method in actions.items(): + menu.addAction(action_text, action_method) + action = menu.exec_(self.book_table.mapToGlobal(pos)) + if action: + actions[action.text()]() + + def copy_selected_item(self): + selected_item = self.book_table.currentItem() + if selected_item: + clipboard = QApplication.clipboard() + clipboard.setText(selected_item.text(self.book_table.currentColumn())) + + def edit_selected_item(self): + selected_item = self.book_table.currentItem() + if selected_item: + self.edit_cell(selected_item) + + def update_table_item(self, item, old_value, new_value): + column = self.book_table.currentColumn() + column_name = self.book_table.horizontalHeaderItem(column).text() if self.book_table == item.treeWidget() else self.user_table.horizontalHeaderItem(column).text() + + element_id = int(item.text(0)) + if isinstance(item, QTreeWidgetItem) and item.treeWidget() == self.book_table: + element = self.library.get_book_by_id(element_id) + elif isinstance(item, QTreeWidgetItem) and item.treeWidget() == self.user_table: + element = self.user_gestion.get_user_by_id(element_id) + + if column == 1: + element.title = new_value + elif column == 2: + element.author = new_value + elif column == 3: + element.publisher = new_value + elif column == 4: + element.isbn = new_value + elif column == 5: + element.total_copies = int(new_value) if self.book_table == item.treeWidget() else None + element.take = new_value if self.user_table == item.treeWidget() else None + elif column == 6: + element.available_copies = int(new_value) if self.book_table == item.treeWidget() else None + + self.library.update_book(element) if self.book_table == item.treeWidget() else self.user_gestion.update_user( + element) + + def open_add_book_dialog(self): + add_book_dialog = AddBookDialog(self) + add_book_dialog.set_library_app_reference(self) + add_book_dialog.exec_() + + def open_add_user_dialog(self): + add_user_dialog = AddUserDialog(self) + add_user_dialog.set_library_app_reference(self) + add_user_dialog.exec_() + + def update_tables(self): + self.update_book_table() + self.update_user_table() + + def update_book_table(self): + self.book_table.setRowCount(0) + for book in self.library.get_books(): + self.add_book_to_table(book) + + def update_user_table(self): + self.user_table.setRowCount(0) + for user in self.user_gestion.get_users(): + self.add_user_to_table(user) + + def add_book_to_table(self, book): + row_position = self.book_table.rowCount() + self.book_table.insertRow(row_position) + self.book_table.setItem(row_position, 0, QTableWidgetItem(str(book.id))) + self.book_table.setItem(row_position, 1, QTableWidgetItem(book.title)) + self.book_table.setItem(row_position, 2, QTableWidgetItem(book.author)) + self.book_table.setItem(row_position, 3, QTableWidgetItem(book.publisher)) + self.book_table.setItem(row_position, 4, QTableWidgetItem(book.isbn)) + self.book_table.setItem(row_position, 5, QTableWidgetItem(str(book.total_copies))) + self.book_table.setItem(row_position, 6, QTableWidgetItem(str(book.available_copies))) + + def add_user_to_table(self, user): + row_position = self.user_table.rowCount() + self.user_table.insertRow(row_position) + self.user_table.setItem(row_position, 0, QTableWidgetItem(str(user.id))) + self.user_table.setItem(row_position, 1, QTableWidgetItem(user.first_name)) + self.user_table.setItem(row_position, 2, QTableWidgetItem(user.last_name)) + self.user_table.setItem(row_position, 3, QTableWidgetItem(user.email)) + self.user_table.setItem(row_position, 4, QTableWidgetItem(user.address)) + self.user_table.setItem(row_position, 5, QTableWidgetItem(str(user.borrowed_books))) + + def edit_cell(self, item): + row, column = item.row(), item.column() + old_value = item.text() + new_value, ok = QInputDialog.getText(self, "Édition de cellule", + f"Entrez une nouvelle valeur pour {self.book_table.horizontalHeaderItem(column).text()}:" if + self.book_table == item.treeWidget() else f"Entrez une nouvelle valeur pour {self.user_table.horizontalHeaderItem(column).text()}", + QLineEdit.Normal, old_value) + if ok and new_value != old_value: + item.setText(new_value) + self.update_table_item(item, old_value, new_value) + + def import_from_csv(self): + file_dialog = QFileDialog() + file_path, _ = file_dialog.getOpenFileName(self, "Importer depuis un fichier CSV", "", "CSV Files (*.csv)") + if file_path: + self.library.import_from_csv(file_path) + self.update_tables() + + def export_to_csv(self): + file_dialog = QFileDialog() + file_path, _ = file_dialog.getSaveFileName(self, "Exporter vers un fichier CSV", "", "CSV Files (*.csv)") + if file_path: + self.library.export_to_csv(file_path) + + def take_book(self): + book_id = self.entry_take.text() + if book_id: + book_id = int(book_id) + success = self.library.take_book(book_id) + if success: + self.update_tables() + else: + QMessageBox.warning(self, "Emprunt de livre", + f"Impossible d'emprunter le livre avec l'ID {book_id}.") + + def return_book(self): + book_id = self.entry_return.text() + if book_id: + book_id = int(book_id) + success = self.library.return_book(book_id) + if success: + self.update_tables() + else: + QMessageBox.warning(self, "Retour de livre", + f"Impossible de retourner le livre avec l'ID {book_id}.") + + def remove_book(self): + book_id = self.entry_remove.text() + if book_id: + book_id = int(book_id) + success = self.library.remove_book(book_id) + if success: + self.update_tables() + else: + QMessageBox.warning(self, "Suppression de livre", + f"Impossible de supprimer le livre avec l'ID {book_id}.") + + +if __name__ == "__main__": + app = QApplication([]) + window = LibraryApp() + window.show() + app.exec_() diff --git a/tools/dialog_logic.py b/tools/dialog_logic.py new file mode 100644 index 0000000..6c29c7d --- /dev/null +++ b/tools/dialog_logic.py @@ -0,0 +1,115 @@ +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout, + QHBoxLayout, QWidget, QComboBox, QMessageBox, QFileDialog, QTreeWidget, + QTreeWidgetItem, QTableWidgetItem, QHeaderView, QDialog, QAbstractItemView, QMenu, QCheckBox, QInputDialog) +from logs import * +from library_logic import * + +class AddBookDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Ajouter un livre") + self.resize(300, 100) + + self.layout = QVBoxLayout() + + self.entry_title = QLineEdit() + self.entry_title.setPlaceholderText("Titre du livre") + self.layout.addWidget(self.entry_title) + + self.entry_author = QLineEdit() + self.entry_author.setPlaceholderText("Auteur") + self.layout.addWidget(self.entry_author) + + self.entry_publisher = QLineEdit() # Champ pour la maison d'édition + self.entry_publisher.setPlaceholderText("Maison d'édition") + self.layout.addWidget(self.entry_publisher) + + self.entry_isbn = QLineEdit() + self.entry_isbn.setPlaceholderText("ISBN") + self.layout.addWidget(self.entry_isbn) + + self.entry_copies = QLineEdit() + self.entry_copies.setPlaceholderText("Nombre d'exemplaires") + self.layout.addWidget(self.entry_copies) + + buttons_layout = QHBoxLayout() + + self.btn_add = QPushButton("Ajouter") + self.btn_add.clicked.connect(self.add_book_to_list) + buttons_layout.addWidget(self.btn_add) + + self.btn_cancel = QPushButton("Annuler") + self.btn_cancel.clicked.connect(self.close) + buttons_layout.addWidget(self.btn_cancel) + + self.layout.addLayout(buttons_layout) + self.setLayout(self.layout) + +# self.secondary_books_table = QTreeWidget() +# self.secondary_books_table.setColumnCount(4) +# self.secondary_books_table.setHeaderLabels(["ID", "Titre", "Auteur", "Maison d'édition", "ISBN", "Exemplaires", "Exemplaires Diponibles"]) +# self.layout.addWidget(self.secondary_books_table) + + + def add_book_to_list(self): + if self.library_app.library.books: + last_book = max(self.library_app.library.books, key=lambda x: x.book_id) + new_book_id = last_book.book_id + 1 + else: + new_book_id = 1 + + title = self.entry_title.text() + author = self.entry_author.text() + publisher = self.entry_publisher.text() + isbn = self.entry_isbn.text() + total_copies = self.entry_copies.text() + available_copies = total_copies + + if title and author and publisher and isbn and total_copies: + new_book = Book( + new_book_id, + title, + author, + publisher, + isbn, + int(total_copies), + int(available_copies) + ) + + self.library_app.library.add(new_book) + log_action(f"Ajout d'un livre : ID={new_book_id}, Titre='{title}', Auteur='{author}', ISBN='{isbn}'", success=True) + books = self.library_app.library.display_books() + self.library_app.update_book_table(books) + + # Effacer les champs après l'ajout du livre + self.entry_title.clear() + self.entry_author.clear() + self.entry_publisher.clear() + self.entry_isbn.clear() + self.entry_copies.clear() + +class ConfirmationDialog(QDialog): + def __init__(self, message, parent=None): + super().__init__(parent) + self.setWindowTitle("Confirmation de suppression") + self.resize(300, 150) + + layout = QVBoxLayout() + + # Définir l'icône de l'application dans la barre des tâches + self.setWindowIcon(QIcon('icon.png')) + + self.message_label = QLabel(message) + layout.addWidget(self.message_label) + + self.never_show_checkbox = QCheckBox("Ne plus afficher") + layout.addWidget(self.never_show_checkbox) + + self.ok_button = QPushButton("OK") + self.ok_button.clicked.connect(self.accept) + layout.addWidget(self.ok_button) + + self.setStyleSheet(load_stylesheet('content\css\style_confirmeDialog.css')) + + self.setLayout(layout) diff --git a/tools/library_logic.py b/tools/library_logic.py new file mode 100644 index 0000000..07fb8ed --- /dev/null +++ b/tools/library_logic.py @@ -0,0 +1,124 @@ +# library_logic.py + +import csv +import uuid # Importer le module uuid +from logs import * + +class Book: + def __init__(self, book_id, title, author, publisher, isbn, total_copies, available_copies): + self.book_id = book_id + self.title = title + self.author = author + self.isbn = isbn + self.total_copies = total_copies + self.available_copies = available_copies + self.publisher = publisher + +class Library: + def __init__(self): + self.books = [] + self.primary_key_counter = 1 # Compteur pour générer des identifiants uniques numériques + + def add(self, book): + self.books.append(book) + log_action(f"Ajout d'un livre : ID={book.book_id}, Titre='{book.title}', Auteur='{book.author}', ISBN='{book.isbn}'", success=True) + + def add_multiple(self, books_to_add): + self.books.extend(books_to_add) + + def take_book_by_id(self, book_id): + for book in self.books: + if int(book.book_id) == int(book_id): + if book.available_copies > 0: + book.available_copies -= 1 + log_action("Vous avez emprunté '{book.title}'") + return True, f"Vous avez emprunté '{book.title}'" + else: + log_action(f"Plus de copies disponibles pour '{book.title}'") + return False, f"Plus de copies disponibles pour '{book.title}'" + log_action("Livre non trouvé") + return False, "Livre non trouvé" + + def return_book_by_id(self, book_id): + for book in self.books: + if int(book.book_id) == int(book_id): + if book.available_copies < book.total_copies: + book.available_copies += 1 + return True, f"Vous avez retourné '{book.title}'" + else: + return False, f"Toutes les copies de '{book.title}' sont déjà retournées" + return False, "Livre non trouvé" + + def remove_book_by_id(self, book_id): + for book in self.books: + if int(book.book_id) == int(book_id): + self.books.remove(book) + log_action(f"Suppression d'un livre : ID={book.book_id}, Titre='{book.title}', Auteur='{book.author}', ISBN='{book.isbn}'", success=True) + return True, f"Le livre '{book.title}' a été supprimé de la bibliothèque" + log_action(f"Tentative de suppression d'un livre avec l'ID='{book_id}'", success=False) + return False, "Livre non trouvé" + + # Méthode pour mettre à jour les détails d'un livre + def update_book_details(self, book): + for index, existing_book in enumerate(self.books): + if existing_book.book_id == book.book_id: + self.books[index] = book # Mettre à jour les détails du livre dans la liste + log_action(f"Mise à jour des détails du livre : ID={book.book_id}, Titre='{book.title}', Auteur='{book.author}', Maison d'édition='{book.publisher}', ISBN='{book.isbn}', Total Copies='{book.total_copies}', Copies Disponibles='{book.available_copies}'", success=True) + break # Sortir de la boucle une fois le livre mis à jour + + # Méthode pour afficher les livres en fonction de différents critères de recherche + def display_books(self, query=None, by_isbn=False, by_author=False, by_title=False, by_total_copies=False, by_available_copies=False, by_publisher=False, by_ID=False): + if query: + filtered_books = [] + for book in self.books: + if (by_isbn and query.lower() in book.isbn.lower()) or \ + (by_author and query.lower() in book.author.lower()) or \ + (by_title and query.lower() in book.title.lower()) or \ + (by_available_copies and query.lower() in str(book.available_copies).lower()) or \ + (by_publisher and query.lower() in book.publisher.lower()) or \ + (by_total_copies and query.lower() in str(book.total_copies).lower()) or \ + (by_ID and query.lower() in str(book.book_id).lower()): + filtered_books.append(book) + return filtered_books + else: + return self.books + + + # Méthode pour importer des livres à partir d'un fichier CSV + def import_from_csv(self, file_path): + try: + with open(file_path, newline='', encoding='utf-8') as file: + csv_reader = csv.DictReader(file) + books_to_add = [] + max_book_id = max([book.book_id for book in self.books], default=0) + + for row in csv_reader: + max_book_id += 1 # Incrémenter l'ID pour le nouveau livre + book = Book( + max_book_id, + row['Title'], + row['Author'], + row['Publisher'], + row['ISBN'], + int(row['Total Copies']), + int(row['Total Copies']) # Disponibilité initiale égale au nombre total de copies + ) + books_to_add.append(book) + log_action(f"Ajout d'un livre : ID={max_book_id}, Titre='{row['Title']}', Auteur='{row['Author']}', Publisher='{row['Publisher']}', ISBN='{row['ISBN']}', max_copies='{int(row['Total Copies'])}', copie_dispo='{int(row['Total Copies'])}'", success=True) + + self.add_multiple(books_to_add) + # Enregistrement de l'action dans les logs + log_action(f"Importation réussie à partir de {file_path}", success=True) + return True # Indiquer que l'importation s'est déroulée avec succès + except Exception as e: + log_action(f"Erreur lors de l'importation depuis {file_path}: {str(e)}", success=False) + print(f"Erreur lors de l'importation : {str(e)}") + return False + + # Méthode pour obtenir un livre par son ID + def get_book_by_id(self, book_id): + for book in self.books: + if book.book_id == int(book_id): + return book + log_action(f"Aucun livre trouvé avec l'ID={book_id}", success=False) + return None # Si aucun livre correspondant à l'ID n'est trouvé diff --git a/tools/logs.py b/tools/logs.py new file mode 100644 index 0000000..5f00f33 --- /dev/null +++ b/tools/logs.py @@ -0,0 +1,23 @@ +# logs.py + +import os +from datetime import datetime + +def create_log_directory(): + log_directory = 'data/logs/' + if not os.path.exists(log_directory): + os.makedirs(log_directory) + +def get_log_filename(): + now = datetime.now() + log_directory = 'data/logs/' + return f"{log_directory}{now.strftime('%Y-%m-%d_%H')}.log" + +def log_action(action, success=True, error_message=None, empr=False): + log_filename = get_log_filename() + with open(log_filename, 'a') as log_file: + log_file.write(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Action: {action} - Success: {'Yes' if success else 'No'} - Error: {error_message}\n") + if not success and error_message: + log_file.write(f"\n") + if empr==True: + print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Action: {action} - Success: {'Yes' if success else 'No'} - Error: {error_message}") diff --git a/tools/menu_setup.py b/tools/menu_setup.py new file mode 100644 index 0000000..853e628 --- /dev/null +++ b/tools/menu_setup.py @@ -0,0 +1,82 @@ +from logs import * +from PyQt5.QtWidgets import QAction, QMenuBar +from PyQt5.QtGui import QIcon + +def create_menu(window): + # Menu Fichier + file_menu = window.menuBar().addMenu("Fichier") + + open_action = QAction(QIcon(), "Importer", window) + open_action.setShortcut("Ctrl+O") + open_action.triggered.connect(window.import_from_csv) + log_action("Importation d'un fichier depuis le menu fichier") + file_menu.addAction(open_action) + + # save_action = QAction(QIcon(), "Enregistrer", window) + # save_action.setShortcut("Ctrl+S") + # save_action.triggered.connect(window.save_file) + # file_menu.addAction(save_action) + + save_as_action = QAction(QIcon(), "Enregistrer sous", window) + save_as_action.setShortcut("Ctrl+Shift+S") + save_as_action.triggered.connect(window.export_to_csv) + log_action("Exportation du fichier depuis le menu fichier") + file_menu.addAction(save_as_action) + + file_menu.addSeparator() + + print_action = QAction(QIcon(), "Imprimer toute la librairie", window) + print_action.setShortcut("Ctrl+P") + print_action.triggered.connect(window.print_all_files) + log_action("Impression de la librairie depuis le menu fichier") + file_menu.addAction(print_action) + + print_action = QAction(QIcon(), "Imprimer un livre par ID", window) + print_action.setShortcut("Ctrl+Shift+P") + print_action.triggered.connect(window.print_selected_file) + log_action("Impression d'un livre depuis le menu fichier") + file_menu.addAction(print_action) + + file_menu.addSeparator() + + quit_action = QAction(QIcon(), "Quitter", window) + quit_action.setShortcut("Ctrl+Q") + quit_action.triggered.connect(window.close) + log_action("Fermeture de l'application depuis le menu fichier") + file_menu.addAction(quit_action) + + # Menu Option + option_menu = window.menuBar().addMenu("Option") + + fullscreen = QAction(QIcon(), "Pleine écran", window) + fullscreen.setShortcut("F11") + fullscreen.triggered.connect(window.toggle_fullscreen) + log_action("Mise en pleine écran/Mise en fenêtré") + option_menu.addAction(fullscreen) + + option_menu.addSeparator() + + vider_cache = QAction(QIcon(), "Vider le cache", window) + vider_cache.setShortcut("Ctrl+Shift+D") + vider_cache.triggered.connect(window.supprimer_logs) + log_action("Suppression logs") + option_menu.addAction(vider_cache) + + # Menu Édition + # edit_menu = window.menuBar().addMenu("Édition") + # undo_action = QAction(QIcon(), "Annuler", window) + # undo_action.setShortcut("Ctrl+Z") + # undo_action.triggered.connect(window.text_edit.undo) + # edit_menu.addAction(undo_action) + # redo_action = QAction(QIcon(), "Rétablir", window) + # redo_action.setShortcut("Ctrl+Y") + # redo_action.triggered.connect(window.text_edit.redo) + # edit_menu.addAction(redo_action) + # copy_action = QAction(QIcon(), "Copier", window) + # copy_action.setShortcut("Ctrl+C") + # copy_action.triggered.connect(window.text_edit.copy) + # edit_menu.addAction(copy_action) + # paste_action = QAction(QIcon(), "Coller", window) + # paste_action.setShortcut("Ctrl+V") + # paste_action.triggered.connect(window.text_edit.paste) + # edit_menu.addAction(paste_action) diff --git a/tools/user_logic.py b/tools/user_logic.py new file mode 100644 index 0000000..b99aff6 --- /dev/null +++ b/tools/user_logic.py @@ -0,0 +1,89 @@ +# user_logic.py + +import csv +import uuid # Importer le module uuid +from logs import * + +class User: + def __init__(self, user_id, username, name, email, empreint=0, address=""): + self.user_id = user_id + self.fname = username + self.name = name + self.mail = email + self.take = empreint + self.address = address + +class User_gestion: + def __init__(self): + self.users = [] + self.primary_key_counter = 1 # Compteur pour générer des identifiants uniques numériques + + def add(self, user): + self.users.append(user) + log_action(f"Ajout d'un utilisateur : ID={user.user_id}, Fname='{user.fname}', name='{user.name}, Email='{user.mail}', Empreint={user.take}", success=True) + + def add_multiple(self, users_to_add): + self.users.extend(users_to_add) + + # Méthode pour mettre à jour les détails d'un utilisateur + def update_user_details(self, user): + for index, existing_user in enumerate(self.users): + if existing_user.user_id == user.user_id: + self.users[index] = user # Mettre à jour les détails du utilisateur dans la liste + log_action(f"Mise à jour des détails du utilisateur : ID={user.user_id}, Fname='{user.fname}', name='{user.name}, Email='{user.mail}', Empreint={user.take}", success=True) + break # Sortir de la boucle une fois le utilisateur mis à jour + + # Méthode pour afficher les utilisateurs en fonction de différents critères de recherche + def display_users(self, query=None, by_fname=False, by_name=False, by_mail=False, by_address=False, by_take=False): + if query: + filtered_users = [] + for user in self.users: + if (by_fname and query.lower() in user.fname.lower()) or \ + (by_name and query.lower() in user.name.lower()) or \ + (by_mail and query.lower() in user.mail.lower()) or \ + (by_address and query.lower() in user.address.lower()) or \ + (by_take and query.lower() in str(user.take).lower()): + filtered_users.append(user) + return filtered_users + else: + return self.users + + + # Méthode pour importer des utilisateurs à partir d'un fichier CSV + def import_from_csv(self, file_path): + try: + with open(file_path, newline='', encoding='utf-8') as file: + csv_reader = csv.DictReader(file) + users_to_add = [] + max_user_id = max([user.user_id for user in self.users], default=0) + + for row in csv_reader: + max_user_id += 1 + user = User( + max_user_id, + row['Fname'], + row['Name'], + row['Email'], + row.get('Address', ''), # Adjusted to reflect the new column order + int(row['Empreint']) + ) + users_to_add.append(user) + log_action(f"Ajout d'un utilisateur : ID={max_user_id}, First Name='{row['Fname']}', Name='{row['Name']}', Email='{row['Email']}', Address='{row.get('Address', '')}', Nombre d'empreint='{int(row['Empreint'])}'", success=True) + + self.add_multiple(users_to_add) + log_action(f"Importation réussie à partir de {file_path}", success=True) + return True + except Exception as e: + log_action(f"Erreur lors de l'importation depuis {file_path}: {str(e)}", success=False) + print(f"Erreur lors de l'importation : {str(e)}") + return False + + # Méthode pour obtenir un utilisateur par son ID + def get_user_by_id(self, user_id): + for user in self.users: + if user.user_id == user_id: + return user + log_action(f"Aucun utilisateur trouvé avec l'ID={user_id}", success=False) + return None # Si aucun utilisateur correspondant à l'ID n'est trouvé + +