[main] Started working on member list

This commit is contained in:
Thastertyn 2025-01-14 17:22:35 +01:00
parent 8e6a6e8693
commit 85d313d45b
12 changed files with 384 additions and 24 deletions

0
src/database/__init__.py Normal file
View File

14
src/database/member.py Normal file
View File

@ -0,0 +1,14 @@
from sqlalchemy.exc import IntegrityError
from models.member import Member
from utils.database import DatabaseManager
def create_new_member(new_member: Member):
try:
with DatabaseManager.get_session() as session:
session.add(new_member)
session.commit()
except IntegrityError as e:
print(e)
session.rollback()

View File

@ -2,11 +2,10 @@ from .base import Base
from .author import Author from .author import Author
from .book import Book from .book import Book
from .book_category import BookCategory from .book_category import BookCategory
from .book_overview import BooksOverview
from .book_category_link import BookCategoryLink from .book_category_link import BookCategoryLink
from .member import Member from .member import Member
from .librarian import Librarian from .librarian import Librarian
from .loan import Loan from .loan import Loan
from .book_overview import BooksOverview __all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BooksOverview"]
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan", "BookOverviewView"]

View File

@ -5,6 +5,10 @@ from PySide6.QtGui import QRegularExpressionValidator
from PySide6.QtCore import QRegularExpression from PySide6.QtCore import QRegularExpression
from models.book import Book, BookStatusEnum from models.book import Book, BookStatusEnum
from utils.database import DatabaseManager
from sqlalchemy import update
class BookEditor(QDialog): class BookEditor(QDialog):
def __init__(self, book: Book, parent=None): def __init__(self, book: Book, parent=None):
super().__init__(parent) super().__init__(parent)
@ -47,12 +51,6 @@ class BookEditor(QDialog):
self.categories_input = QLineEdit(all_categories) self.categories_input = QLineEdit(all_categories)
form_layout.addRow("Categories: ", self.categories_input) form_layout.addRow("Categories: ", self.categories_input)
# Status dropdown
self.status_input = QComboBox()
self.status_input.addItems([status.value for status in BookStatusEnum])
self.status_input.setCurrentText(self.book.status.value)
form_layout.addRow("Status:", self.status_input)
layout.addLayout(form_layout) layout.addLayout(form_layout)
# Buttons # Buttons
@ -76,5 +74,15 @@ class BookEditor(QDialog):
self.book.isbn = self.isbn_input.text() self.book.isbn = self.isbn_input.text()
self.book.status = BookStatusEnum(self.status_input.currentText()) self.book.status = BookStatusEnum(self.status_input.currentText())
with DatabaseManager.get_session() as session:
existing_book = session.query(Book).get(self.book.id)
existing_book.title = self.book.title
existing_book.description = self.book.description
existing_book.year_published = self.book.year_published
existing_book.isbn = self.book.isbn
session.commit()
# Accept the dialog and close # Accept the dialog and close
self.accept() self.accept()

View File

@ -129,7 +129,9 @@ class BookCard(QWidget):
print("Remove reservation selected") print("Remove reservation selected")
def delete_book(self): def delete_book(self):
print("Delete") if not self.make_sure():
return
with DatabaseManager.get_session() as session: with DatabaseManager.get_session() as session:
try: try:
stmt = delete(Book).where(Book.id == self.book_overview.id) stmt = delete(Book).where(Book.id == self.book_overview.id)
@ -139,3 +141,17 @@ class BookCard(QWidget):
except Exception as e: except Exception as e:
session.rollback session.rollback
print(e) print(e)
def make_sure(self) -> bool:
are_you_sure_box = QMessageBox()
are_you_sure_box.setIcon(QMessageBox.Question)
are_you_sure_box.setWindowTitle("Are you sure?")
are_you_sure_box.setText(f"Are you sure you want to delete {self.book_overview.title}?")
are_you_sure_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
are_you_sure_box.setDefaultButton(QMessageBox.No)
# Show the message box and capture the user's response
response = are_you_sure_box.exec()
# Handle the response
return response == QMessageBox.Yes

View File

@ -10,6 +10,8 @@ from models.book_overview import BooksOverview
from utils.database import DatabaseManager from utils.database import DatabaseManager
from ui.member_editor.member_editor import MemberEditor
class LibraryDashboard(QWidget): class LibraryDashboard(QWidget):
def __init__(self): def __init__(self):
@ -74,8 +76,7 @@ class LibraryDashboard(QWidget):
card.setVisible(title_contains_text or author_name_contains_text or isbn_contains_text) card.setVisible(title_contains_text or author_name_contains_text or isbn_contains_text)
def register_member(self): def register_member(self):
QMessageBox.information( MemberEditor().exec()
self, "Add Member", "Open dialog to register a new member.")
def add_borrow_record(self): def add_borrow_record(self):
QMessageBox.information(self, "Add Borrow Record", QMessageBox.information(self, "Add Borrow Record",

View File

View File

@ -0,0 +1,143 @@
from PySide6.QtWidgets import (
QHBoxLayout, QVBoxLayout, QLabel, QWidget, QMenu, QSizePolicy, QLayout, QMessageBox
)
from PySide6.QtGui import Qt
from PySide6.QtCore import qDebug
from models.member import Member, MemberStatusEnum
from utils.database import DatabaseManager
from sqlalchemy import delete
STATUS_TO_COLOR_MAP = {
MemberStatusEnum.active: "#3c702e",
MemberStatusEnum.inactive: "#702525"
}
class MemberCard(QWidget):
def __init__(self, member: Member):
super().__init__()
self.member = member
self.setAttribute(Qt.WidgetAttribute.WA_Hover, True)
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
# Set initial stylesheet with hover behavior
self.setStyleSheet("""
MemberCard:hover {
background-color: palette(highlight);
}
""")
# Layout setup
layout = QHBoxLayout(self)
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(10)
# Left-side content
left_side = QVBoxLayout()
layout.addLayout(left_side)
name_label = QLabel(f"{member.first_name} {member.last_name}")
name_label.setStyleSheet("font-size: 20px; font-weight: bold;")
email_label = QLabel(f"Email: {member.email}")
phone_label = QLabel(f"Phone: {member.phone or 'Not Available'}")
left_side.addWidget(name_label)
left_side.addWidget(email_label)
left_side.addWidget(phone_label)
# Right-side content
right_side = QVBoxLayout()
layout.addLayout(right_side)
status_label = QLabel(str(member.status.value.capitalize()))
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[member.status]}; font-size: 20px; font-weight: bold;")
status_label.setAlignment(Qt.AlignmentFlag.AlignRight)
register_date_label = QLabel(f"Registered: {member.register_date}")
register_date_label.setAlignment(Qt.AlignmentFlag.AlignRight)
right_side.addWidget(status_label)
right_side.addWidget(register_date_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):
context_menu = QMenu(self)
action_edit_member = context_menu.addAction("Edit Member")
action_deactivate_member = context_menu.addAction("Deactivate Member")
action_activate_member = context_menu.addAction("Activate Member")
context_menu.addSeparator()
delete_member_action = context_menu.addAction("Delete Member")
delete_member_action.triggered.connect(self.delete_member)
if self.member.status == MemberStatusEnum.active:
action_activate_member.setVisible(False)
elif self.member.status == MemberStatusEnum.inactive:
action_deactivate_member.setVisible(False)
action = context_menu.exec_(self.mapToGlobal(event.pos()))
if action == action_edit_member:
print("Edit Member selected") # Implement editor logic here
elif action == action_deactivate_member:
self.update_member_status(MemberStatusEnum.inactive)
elif action == action_activate_member:
self.update_member_status(MemberStatusEnum.active)
def delete_member(self):
self.make_sure()
# if not self.make_sure():
# return
# with DatabaseManager.get_session() as session:
# try:
# stmt = delete(Member).where(Member.id == self.member.id)
# session.execute(stmt)
# session.commit()
# self.setVisible(False)
# except Exception as e:
# session.rollback()
# print(e)
def update_member_status(self, new_status):
with DatabaseManager.get_session() as session:
try:
member = session.query(Member).filter(Member.id == self.member.id).one_or_none()
if member:
member.status = new_status
session.commit()
QMessageBox.information(self, "Status Updated", f"Member status updated to {new_status.value.capitalize()}.")
self.member.status = new_status
self.update_status_label()
else:
QMessageBox.critical(self, "Error", "The member you requested could not be found.", QMessageBox.StandardButton.Ok)
except Exception as e:
session.rollback()
print(e)
def update_status_label(self):
self.findChild(QLabel, self.member.status.value).setStyleSheet(
f"color: {STATUS_TO_COLOR_MAP[self.member.status]}; font-size: 20px; font-weight: bold;"
)
def make_sure(self) -> bool:
are_you_sure_box = QMessageBox()
are_you_sure_box.setIcon(QMessageBox.Question)
are_you_sure_box.setWindowTitle("Are you sure?")
are_you_sure_box.setText(f"Are you sure you want to delete {self.member.first_name} {self.member.last_name}?")
are_you_sure_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
are_you_sure_box.setDefaultButton(QMessageBox.No)
response = are_you_sure_box.exec()
return response == QMessageBox.Yes

View File

@ -0,0 +1,114 @@
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea,
QPushButton, QMessageBox, QVBoxLayout
)
from PySide6.QtCore import Qt
from .member_card import MemberCard
from models.member import Member
from utils.database import DatabaseManager
from ui.member_editor.member_editor import MemberEditor
class MemberDashboard(QWidget):
def __init__(self):
super().__init__()
# Central widget and layout
main_layout = QVBoxLayout(self)
# Title label
title_label = QLabel("Member Dashboard", self)
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet(
"font-size: 20px; font-weight: bold; color: #0078D4;")
main_layout.addWidget(title_label)
# Search bar
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Search members...")
self.search_input.textChanged.connect(self.filter_members)
main_layout.addWidget(self.search_input)
# Scrollable area for cards
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
# 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 default spacing
# Align the cards to the top
self.scroll_layout.setAlignment(Qt.AlignTop)
self.members = []
self.member_cards = []
self.redraw_cards()
self.scroll_widget.setLayout(self.scroll_layout)
self.scroll_area.setWidget(self.scroll_widget)
main_layout.addWidget(self.scroll_area)
# Buttons for actions
button_layout = QHBoxLayout()
register_member_button = QPushButton("Add New Member")
register_member_button.clicked.connect(self.register_member)
button_layout.addWidget(register_member_button)
delete_member_button = QPushButton("Delete Member")
delete_member_button.clicked.connect(self.delete_member)
button_layout.addWidget(delete_member_button)
main_layout.addLayout(button_layout)
def filter_members(self, text):
"""Filter the cards based on the search input."""
for card, member in zip(self.member_cards, self.members):
name_contains_text = text.lower() in member.name.lower()
id_contains_text = text.lower() in str(member.id)
card.setVisible(name_contains_text or id_contains_text)
def register_member(self):
MemberEditor().exec()
def delete_member(self):
QMessageBox.information(self, "Delete Member",
"Open dialog to delete a member.")
def clear_layout(self, layout):
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
sub_layout = item.layout()
if sub_layout is not None:
self.clear_layout(sub_layout)
del item
def redraw_cards(self):
self.clear_layout(self.scroll_layout)
self.member_cards = []
self.members = self.fetch_members_from_db()
for member in self.members:
card = MemberCard(member)
self.scroll_layout.addWidget(card)
self.member_cards.append(card)
def fetch_members_from_db(self):
"""Fetch all members from the database."""
try:
with DatabaseManager.get_session() as session:
members = session.query(Member).all()
return members
except Exception as e:
QMessageBox.critical(self, "Database Error",
f"Failed to fetch members: {e}")
return []

View File

@ -1,19 +1,81 @@
from PySide6.QtGui import QGuiApplication, QAction from PySide6.QtGui import QGuiApplication, QAction
from PySide6.QtQml import QQmlApplicationEngine from PySide6.QtQml import QQmlApplicationEngine
from PySide6 import QtWidgets, QtCore from PySide6 import QtWidgets, QtCore
from PySide6.QtWidgets import QVBoxLayout, QFormLayout, QLineEdit, QHBoxLayout, QPushButton, QDialog
class MemberEditor(QtWidgets.QWidget): from models.member import Member
def __init__(self):
from database.member import create_new_member
class MemberEditor(QDialog):
def __init__(self, member: Member = None):
super().__init__() super().__init__()
# Central widget and layout self.create_layout()
main_layout = QtWidgets.QVBoxLayout(self)
# Title label if member:
title_label = QtWidgets.QLabel("Members", self) self.member = member
title_label.setAlignment(QtCore.Qt.AlignCenter) self.fill_with_existing_data()
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;") self.create_new = False
main_layout.addWidget(title_label) else:
self.member = Member()
self.create_new = True
self.setLayout(main_layout) def create_layout(self):
self.setWindowTitle("Members")
self.setMinimumSize(400, 300)
# Create main layout
self.layout = QVBoxLayout(self)
# Form layout for member fields
self.form_layout = QFormLayout()
# First name field
self.first_name_input = QLineEdit()
self.form_layout.addRow("First name:", self.first_name_input)
# Last name field
self.last_name_input = QLineEdit()
self.form_layout.addRow("Last name: ", self.last_name_input)
# E-mail field
self.email_input = QLineEdit()
self.form_layout.addRow("E-mail:", self.email_input)
# Phone number
self.phone_number_input = QLineEdit()
self.form_layout.addRow("Phone number:", self.phone_number_input)
self.layout.addLayout(self.form_layout)
# Buttons
self.button_layout = QHBoxLayout()
self.save_button = QPushButton("Save")
self.save_button.clicked.connect(self.save_member)
self.button_layout.addWidget(self.save_button)
self.cancel_button = QPushButton("Discard")
self.cancel_button.clicked.connect(self.reject)
self.button_layout.addWidget(self.cancel_button)
self.layout.addLayout(self.button_layout)
def fill_with_existing_data(self):
self.first_name_input.setText(self.member.first_name)
self.last_name_input.setText(self.member.last_name)
self.email_input.setText(self.member.email)
self.phone_number_input.setText(self.member.phone)
def save_member(self):
self.member.first_name = self.first_name_input.text()
self.member.last_name = self.last_name_input.text()
self.member.email = self.email_input.text()
self.member.phone = self.phone_number_input.text()
if self.create_new:
create_new_member(self.member)
self.accept()

View File

@ -5,6 +5,7 @@ from PySide6.QtWidgets import QMessageBox, QFileDialog
from PySide6.QtCore import QStandardPaths from PySide6.QtCore import QStandardPaths
from ui.dashboard.dashboard import LibraryDashboard from ui.dashboard.dashboard import LibraryDashboard
from ui.main_window_tabs.member_list.member_list import MemberDashboard
from ui.book_editor.book_editor import BookEditor from ui.book_editor.book_editor import BookEditor
from ui.member_editor.member_editor import MemberEditor from ui.member_editor.member_editor import MemberEditor
@ -39,8 +40,9 @@ class LibraryWindow(QtWidgets.QMainWindow):
self.dashboard = LibraryDashboard() self.dashboard = LibraryDashboard()
self.member_list = MemberDashboard()
central_widget.addTab(self.dashboard, "Dashboard") central_widget.addTab(self.dashboard, "Dashboard")
central_widget.addTab(MemberEditor(), "Members") central_widget.addTab(self.member_list, "Members")
self.file_types = { self.file_types = {
"XML files (*.xml)": ".xml", "XML files (*.xml)": ".xml",
@ -211,7 +213,8 @@ class LibraryWindow(QtWidgets.QMainWindow):
pass pass
def new_member(self): def new_member(self):
pass MemberEditor().exec()
def import_data(self): def import_data(self):
pass pass