Compare commits

..

No commits in common. "12828b268851dd07f324292b756638871a3905dc" and "a2ffd8aa3b8717571f93b501d5e8340f754dc3fe" have entirely different histories.

8 changed files with 74 additions and 139 deletions

View File

@ -1,5 +1,4 @@
DATABASE_HOST=
DATABASE_PORT=
DATABASE_NAME=
DATABASE_USER=
DATABASE_PASSWORD=
DB_USER=
DB_NAME=
DB_PASSWORD=
DB_HOST=

View File

@ -1,7 +1,6 @@
import sys
import os
import logging
from typing import Optional
from PySide6.QtWidgets import QMessageBox, QApplication
@ -14,7 +13,7 @@ from ui.window import LibraryWindow
class LibraryApp():
def __init__(self, user_config: Optional[UserConfig] = None):
def __init__(self, user_config: UserConfig | None = None):
setup_logger()
self.logger = logging.getLogger(__name__)
self.logger.info("Starting")
@ -38,7 +37,7 @@ class LibraryApp():
self.window.show()
status = self.qt_app.exec()
self.cleanup()
self.logger.info("Exiting")
self.logger.info("Exitting")
return status
def show_error(self, text: str, detail_text: str = ""):

View File

@ -1,40 +1,19 @@
from PySide6.QtGui import QGuiApplication, QAction
from PySide6.QtQml import QQmlApplicationEngine
from PySide6 import QtWidgets, QtCore
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel
from models.book_overview import BooksOverview
class BookEditor(QtWidgets.QWidget):
def __init__(self):
super().__init__()
class BookEditor(QDialog):
def __init__(self, book_overview: BooksOverview, parent=None):
super().__init__(parent)
# Central widget and layout
main_layout = QtWidgets.QVBoxLayout(self)
self.setWindowTitle("Edit a book")
self.setMinimumSize(400, 300)
# Title label
title_label = QtWidgets.QLabel("Books", self)
title_label.setAlignment(QtCore.Qt.AlignCenter)
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
main_layout.addWidget(title_label)
# Create layout
layout = QVBoxLayout(self)
self.setLayout(main_layout)
data_mode_layout = QHBoxLayout()
self.data_mode_label = QLabel("Data mode:")
data_mode_layout.addWidget(self.data_mode_label)
# Buttons
button_layout = QtWidgets.QHBoxLayout()
self.save_button = QtWidgets.QPushButton("Save")
self.save_button.clicked.connect(self.save_book)
button_layout.addWidget(self.save_button)
self.cancel_button = QtWidgets.QPushButton("Discard")
self.cancel_button.clicked.connect(self.reject)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
def save_book(self):
pass

View File

@ -1,11 +1,9 @@
from PySide6.QtGui import QGuiApplication, QAction, Qt
from PySide6.QtGui import QGuiApplication, QAction
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QLabel, QWidget, QMenu, QSizePolicy, QLayout
from PySide6.QtCore import qDebug
from ui.book_editor.book_editor import BookEditor
from models.book import BookStatusEnum
from models.book_overview import BooksOverview
STATUS_TO_COLOR_MAP = {
@ -13,36 +11,22 @@ STATUS_TO_COLOR_MAP = {
BookStatusEnum.borrowed: "#702525",
BookStatusEnum.reserved: "#bc7613"
}
class BookCard(QWidget):
def __init__(self, book_overview: BooksOverview):
super().__init__()
self.book_overview = book_overview
self.setAttribute(Qt.WidgetAttribute.WA_Hover, True) # Enable hover events
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) # Enable styling for background
# Set initial stylesheet with hover behavior
self.setStyleSheet("""
BookCard:hover {
background-color: palette(highlight);
}
""")
# Layout setup
# Create the layout for the card
layout = QHBoxLayout(self)
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize) # Ensure minimum size is respected
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) # Avoid vertical stretching
layout.setContentsMargins(10, 10, 10, 10)
layout.setContentsMargins(10, 10, 10, 10) # Optional: Add margins for spacing
layout.setSpacing(10)
# Left-side content
left_side = QVBoxLayout()
layout.addLayout(left_side)
title_label = QLabel(book_overview.title)
title_label.setStyleSheet("font-size: 20px; font-weight: bold;")
author_label = QLabel("By: " + book_overview.author_name)
isbn_label = QLabel("ISBN: " + (book_overview.isbn or "Not Available"))
left_side.addWidget(title_label)
@ -52,50 +36,33 @@ class BookCard(QWidget):
# Right-side content
right_side = QVBoxLayout()
layout.addLayout(right_side)
status_label = QLabel(str(book_overview.status.value.capitalize()))
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[book_overview.status]}; font-size: 20px; font-weight: bold;")
status_label.setAlignment(Qt.AlignmentFlag.AlignRight)
status_label = QLabel(str(book_overview.status))
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[book_overview.status]}")
right_side.addWidget(status_label)
if book_overview.librarian_name and book_overview.borrower_name:
borrower_label = QLabel("Borrowed: " + book_overview.borrower_name)
borrower_label.setAlignment(Qt.AlignmentFlag.AlignRight)
librarian_label = QLabel("By: " + book_overview.librarian_name)
librarian_label.setAlignment(Qt.AlignmentFlag.AlignRight)
right_side.addWidget(borrower_label)
right_side.addWidget(librarian_label)
self.setLayout(layout)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.contextMenuEvent(event)
else:
super().mousePressEvent(event)
def contextMenuEvent(self, event):
"""Override to create a custom right-click menu."""
context_menu = QMenu(self)
# Add actions to the menu
action_edit_book = context_menu.addAction("Edit Book")
action_edit_author = context_menu.addAction("Edit Author")
action_mark_returned = context_menu.addAction("Mark as Returned")
if self.book_overview.status == BookStatusEnum.borrowed:
action_mark_returned = context_menu.addAction("Mark as Returned")
if self.book_overview.status == BookStatusEnum.reserved:
action_remove_reservation = context_menu.addAction("Remove reservation")
# Execute the menu and get the selected action
action = context_menu.exec_(self.mapToGlobal(event.pos()))
if action == action_edit_book:
BookEditor(self.book_overview).exec()
self.label.setText("Edit Book selected")
elif action == action_edit_author:
print("Edit Author selected")
self.label.setText("Edit Author selected")
elif action == action_mark_returned:
print("Mark as Returned selected")
elif action == action_remove_reservation:
print("Remove reservation selected")
self.label.setText("Mark as Returned selected")

View File

@ -1,16 +1,15 @@
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea,
QFrame, QPushButton, QMessageBox, QVBoxLayout
QFrame, QPushButton, QMessageBox, QVBoxLayout, QScrollArea
)
from PySide6.QtCore import Qt
from .book_card import BookCard
from models.book_overview import BooksOverview
from utils.database import DatabaseManager
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
class LibraryDashboard(QWidget):
def __init__(self):
super().__init__()
@ -21,8 +20,7 @@ class LibraryDashboard(QWidget):
# Title label
title_label = QLabel("Dashboard", self)
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet(
"font-size: 20px; font-weight: bold; color: #0078D4;")
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
main_layout.addWidget(title_label)
# Search bar
@ -38,21 +36,17 @@ class LibraryDashboard(QWidget):
# Container widget for the scroll area
self.scroll_widget = QWidget()
self.scroll_layout = QVBoxLayout(self.scroll_widget)
self.scroll_layout.setSpacing(5) # Set gap between individual cards
self.scroll_layout.setContentsMargins(0, 0, 0, 0) # Remove spacing from all sides which is present by default
# Align the cards to the top
self.scroll_layout.setAlignment(Qt.AlignTop)
self.scroll_layout.setSpacing(5) # Optional: Adjust spacing between cards
self.scroll_layout.setContentsMargins(0, 0, 0, 0)
# Example cards
self.books = self.fetch_books_from_db()
self.book_cards = []
# self.books = self.fetch_books_from_db()
# self.book_cards = []
for book in self.books:
card = BookCard(book)
self.scroll_layout.addWidget(card)
self.book_cards.append(card)
# for book in self.books:
# card = BookCard(book)
# self.scroll_layout.addWidget(card)
# self.book_cards.append(card)
self.scroll_widget.setLayout(self.scroll_layout)
self.scroll_area.setWidget(self.scroll_widget)
@ -73,23 +67,21 @@ class LibraryDashboard(QWidget):
def filter_books(self, text):
"""Filter the cards based on the search input."""
for card, book in zip(self.book_cards, self.books):
card.setVisible(text.lower() in book.title.lower())
card.setVisible(text.lower() in book["title"].lower() or text.lower() in book["description"].lower())
def register_member(self):
QMessageBox.information(
self, "Add Member", "Open dialog to register a new member.")
QMessageBox.information(self, "Add Member", "Open dialog to register a new member.")
def add_borrow_record(self):
QMessageBox.information(self, "Add Borrow Record",
"Open dialog to add a borrow record.")
QMessageBox.information(self, "Add Borrow Record", "Open dialog to add a borrow record.")
def fetch_books_from_db(self):
"""Fetch all books from the database."""
try:
with DatabaseManager.get_session() as session:
books = session.query(BooksOverview).all()
return books
session = SessionLocal()
books = session.query(BooksOverview).all()
return books
except Exception as e:
QMessageBox.critical(self, "Database Error",
f"Failed to fetch books: {e}")
QMessageBox.critical(self, "Database Error", f"Failed to fetch books: {e}")
return []

View File

@ -11,6 +11,12 @@ class SettingsDialog(QtWidgets.QDialog):
self.setWindowTitle("Settings")
self.setMinimumSize(400, 100)
# Position the dialog relative to the parent (main window)
if parent:
x = parent.geometry().x() + parent.geometry().width() // 2 - self.width() // 2
y = parent.geometry().y() + parent.geometry().height() // 2 - self.height() // 2
self.move(x, y)
# Create layout
layout = QtWidgets.QVBoxLayout(self)

View File

@ -26,7 +26,7 @@ class LibraryWindow(QtWidgets.QMainWindow):
self.setCentralWidget(central_widget)
central_widget.addTab(LibraryDashboard(), "Dashboard")
# central_widget.addTab(BookEditor(), "Books")
central_widget.addTab(BookEditor(), "Books")
central_widget.addTab(MemberEditor(), "Members")
@ -74,7 +74,6 @@ class LibraryWindow(QtWidgets.QMainWindow):
# Edit menu
edit_menu = menu_bar.addMenu("Edit")
# Preferences menu
preferences_action = QAction("Preferences", self)
preferences_action.setShortcut("Ctrl+,")
preferences_action.triggered.connect(self.edit_preferences)

View File

@ -1,33 +1,27 @@
import logging
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy import create_engine
from mysql.connector import connect, cursor
from utils.config import DatabaseConfig
from utils.errors.database_config_error import DatabaseConfigError
class DatabaseManager():
_instance: 'DatabaseManager' = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self) -> None:
self.logger = logging.getLogger(__name__)
self.logger.info("Reading database config")
self.database_config = DatabaseConfig()
self.engine = create_engine(f'mysql+mysqlconnector://{self.database_config.user}:{
self.database_config.password}@{self.database_config.host}/{self.database_config.name}')
self.session_local = sessionmaker(bind=self.engine)
self.connection = connect(
host=self.database_config.host,
port=self.database_config.port,
database=self.database_config.name,
user=self.database_config.user,
password=self.database_config.password
)
def get_cursor(self) -> cursor.MySQLCursorAbstract:
return self.connection.cursor(dictionary=True)
def cleanup(self) -> None:
self.logger.debug("Closing connection")
self.engine.dispose()
@classmethod
def get_session(cls) -> Session:
return DatabaseManager._instance.session_local()
self.connection.close()