Compare commits

..

No commits in common. "8db7eae1e411dc4e0bd3b266ddf3a56318660121" and "d242e4ff6860ebc05666157b5f6d17deebada8b1" have entirely different histories.

20 changed files with 116 additions and 286 deletions

View File

@ -1,4 +0,0 @@
DB_USER=
DB_NAME=
DB_PASSWORD=
DB_HOST=

15
.vscode/launch.json vendored
View File

@ -1,15 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/src/app.py",
"console": "integratedTerminal"
}
]
}

View File

@ -37,9 +37,3 @@ My solution will use the following tech stack:
5. **`python-dotenv`** for configurability 5. **`python-dotenv`** for configurability
The problem in question will be demonstrated on a library application The problem in question will be demonstrated on a library application
## Sources
1. [Qt documentation for pyside2](https://doc.qt.io/qtforpython-5/PySide2/QtWidgets)
1. [Qt documentation for PySide6](https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/)
2. [Pythontutorial.net](https://www.pythontutorial.net/pyqt/pyqt-qtoolbar/)
3. [Docs.sqlalchemy.org](https://docs.sqlalchemy.org/)

View File

@ -1,13 +1,11 @@
greenlet==3.1.1 greenlet==3.1.1
mypy==1.14.1 mypy==1.14.1
mypy-extensions==1.0.0 mypy-extensions==1.0.0
mysql-connector-python==9.1.0
PySide6==6.8.1 PySide6==6.8.1
PySide6_Addons==6.8.1 PySide6_Addons==6.8.1
PySide6_Essentials==6.8.1 PySide6_Essentials==6.8.1
python-dotenv==1.0.1 python-dotenv==1.0.1
shiboken2==5.13.2 shiboken2==5.13.2
shiboken6==6.8.1 shiboken6==6.8.1
SQLAlchemy==2.0.36
sqlalchemy-stubs==0.4 sqlalchemy-stubs==0.4
typing_extensions==4.12.2 typing_extensions==4.12.2

View File

@ -1,39 +1,56 @@
import sys # import sys
from PySide6 import QtWidgets, QtCore # from PySide6.QtGui import QGuiApplication
# from PySide6.QtQml import QQmlApplicationEngine
# if __name__ == "__main__":
# app = QGuiApplication(sys.argv)
# engine = QQmlApplicationEngine()
# engine.addImportPath(sys.path[0])
# engine.loadFromModule("ui", "Main")
# if not engine.rootObjects():
# sys.exit(-1)
# exit_code = app.exec()
# del engine
# sys.exit(exit_code)
from ui.dashboard import LibraryDashboard from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models.book import Book, BookStatusEnum
from models.author import Author
if __name__ == "__main__": # Replace with your MySQL database credentials
app = QtWidgets.QApplication([]) DATABASE_URI = 'mysql+mysqlconnector://username:password@localhost:3306/library'
window = LibraryDashboard()
window.show()
sys.exit(app.exec())
# from sqlalchemy import create_engine # Create the engine
# from sqlalchemy.orm import sessionmaker engine = create_engine(DATABASE_URI, echo=True)
# from models.book import Book, BookStatusEnum # Create a configured session class
# from models.author import Author SessionLocal = sessionmaker(bind=engine)
# # Replace with your MySQL database credentials # Create a session instance
# DATABASE_URI = 'mysql+mysqlconnector://username:password@localhost:3306/library' session = SessionLocal()
# # Create the engine new_author = Author(first_name="John", last_name="Doe")
# engine = create_engine(DATABASE_URI)
# from models.base import Base session.add(new_author)
# Base.metadata.create_all(engine) session.commit()
# # Create a configured session class new_book = Book(
# SessionLocal = sessionmaker(bind=engine) title="Sample Book",
description="A fascinating tale.",
year_published="2023",
isbn="1234567890123",
status='available',
author_id=new_author.id
)
# # Create a session instance session.add(new_book)
# session = SessionLocal() session.commit()
# books = session.query(Book).all() books = session.query(Book).all()
# for book in books: for book in books:
# print(book.title, book.author.first_name) print(book.title, book.author.first_name)
# session.close() session.close()

View File

@ -1,10 +1,7 @@
from .base import Base
from .author import Author from .author import Author
from .book import Book
from .book_category import BookCategory from .book_category import BookCategory
from .book import Book
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
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan"]

View File

@ -1,17 +1,15 @@
from sqlalchemy import Column, Integer, String, TIMESTAMP, UniqueConstraint, func from sqlalchemy import Column, Integer, String, TIMESTAMP, UniqueConstraint, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .base import Base Base = declarative_base()
class Author(Base): class Author(Base):
__tablename__ = 'author' __tablename__ = 'author'
__table_args__ = (UniqueConstraint('id'),) __table_args__ = (UniqueConstraint('id'),)
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
first_name = Column(String(50), nullable=False) first_name = Column(String(50), nullable=False)
last_name = Column(String(50), nullable=False) last_name = Column(String(50), nullable=False)
last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now()) last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now())
# Reference 'Book' as a string to avoid direct import
books = relationship('Book', back_populates='author')

View File

@ -1,3 +0,0 @@
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

View File

@ -2,8 +2,12 @@ import enum
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .base import Base from .author import Author
from .book_category import BookCategory
Base = declarative_base()
class BookStatusEnum(enum.Enum): class BookStatusEnum(enum.Enum):
@ -11,20 +15,18 @@ class BookStatusEnum(enum.Enum):
borrowed = 'borrowed' borrowed = 'borrowed'
reserved = 'reserved' reserved = 'reserved'
class Book(Base): class Book(Base):
__tablename__ = 'book' __tablename__ = 'book'
__table_args__ = (UniqueConstraint('isbn'),) __table_args__ = (UniqueConstraint('isbn'),)
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
author_id = Column(Integer, ForeignKey('author.id'), nullable=False) author_id = Column(Integer, ForeignKey('author.id'), nullable=False)
title = Column(String(100), nullable=False) title = Column(String(100), nullable=False)
description = Column(Text, nullable=False) description = Column(Text, nullable=False)
year_published = Column(String(4), nullable=False) year_published = Column(String(4), nullable=False)
isbn = Column(String(13), nullable=False, unique=True) isbn = Column(String(13), nullable=False, unique=True)
status = Column(Enum(BookStatusEnum), nullable=False, default=BookStatusEnum.available) status = Column(Enum(BookStatusEnum), nullable=False, default=BookStatusEnum.available)
created_at = Column(TIMESTAMP, nullable=False, server_default=func.now()) created_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now()) last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now())
# Reference 'Author' as a string to avoid direct import author = relationship('Author', backref='books')
author = relationship('Author', back_populates='books')

View File

@ -1,7 +1,9 @@
from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey, UniqueConstraint, func from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey, UniqueConstraint, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from .base import Base
class BookCategory(Base): class BookCategory(Base):
__tablename__ = 'book_category' __tablename__ = 'book_category'

View File

@ -1,9 +1,12 @@
from sqlalchemy import Column, Integer, ForeignKey from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .book import Book from .book import Book
from .book_category import BookCategory from .book_category import BookCategory
from .base import Base
Base = declarative_base()
class BookCategoryLink(Base): class BookCategoryLink(Base):
__tablename__ = 'book_category_link' __tablename__ = 'book_category_link'

View File

@ -2,8 +2,10 @@ import enum
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .base import Base
Base = declarative_base()
class LibrarianStatusEnum(enum.Enum): class LibrarianStatusEnum(enum.Enum):

View File

@ -2,12 +2,14 @@ import enum
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, Float, func from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, Float, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .base import Base
from .book import Book from .book import Book
from .member import Member from .member import Member
from .librarian import Librarian from .librarian import Librarian
Base = declarative_base()
class LoanStatusEnum(enum.Enum): class LoanStatusEnum(enum.Enum):
borrowed = 'borrowed' borrowed = 'borrowed'

View File

@ -2,14 +2,13 @@ import enum
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from .base import Base
class MemberStatusEnum(enum.Enum): class MemberStatusEnum(enum.Enum):
active = 'active' active = 'active'
inactive = 'inactive' inactive = 'inactive'
Base = declarative_base()
class Member(Base): class Member(Base):
__tablename__ = 'member' __tablename__ = 'member'

32
src/ui/Main.qml Normal file
View File

@ -0,0 +1,32 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
width: 300
height: 200
visible: true
title: "Hello World"
readonly property list<string> texts: ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
function setText() {
var i = Math.round(Math.random() * 3);
text.text = texts[i];
}
ColumnLayout {
anchors.fill: parent
Text {
id: text
text: "Hello World"
Layout.alignment: Qt.AlignHCenter
}
Button {
text: "Click me"
Layout.alignment: Qt.AlignHCenter
onClicked: setText()
}
}
}

View File

View File

@ -1,125 +0,0 @@
from PySide6.QtGui import QGuiApplication, QAction
from PySide6.QtQml import QQmlApplicationEngine
from PySide6 import QtWidgets, QtCore
from ui.settings import SettingsDialog
class LibraryDashboard(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Set up main window properties
self.setWindowTitle("Library Dashboard")
self.setGeometry(100, 100, 400, 400)
# Set up menu bar
self.create_menu_bar()
# Central widget and layout
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
main_layout = QtWidgets.QVBoxLayout(central_widget)
# Title label
title_label = QtWidgets.QLabel("Library Dashboard", self)
title_label.setAlignment(QtCore.Qt.AlignCenter)
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
main_layout.addWidget(title_label)
# Available books list
available_label = QtWidgets.QLabel("Available Books")
available_label.setStyleSheet("font-size: 16px;")
main_layout.addWidget(available_label)
self.available_books_list = QtWidgets.QListWidget()
self.available_books_list.addItems(["Book One", "Book Two", "Book Three", "Book Four","Book One", "Book Two", "Book Three", "Book Four","Book One", "Book Two", "Book Three", "Book Four"])
self.available_books_list.itemClicked.connect(self.edit_book)
main_layout.addWidget(self.available_books_list)
# Borrowed books list
borrowed_label = QtWidgets.QLabel("Currently Borrowed Books")
borrowed_label.setStyleSheet("font-size: 16px;")
main_layout.addWidget(borrowed_label)
self.borrowed_books_list = QtWidgets.QListWidget()
self.borrowed_books_list.addItems(["Book Two", "Book Four"])
self.borrowed_books_list.itemClicked.connect(self.return_book)
main_layout.addWidget(self.borrowed_books_list)
# Buttons for actions
button_layout = QtWidgets.QHBoxLayout()
register_member_button = QtWidgets.QPushButton("Add New Member")
register_member_button.clicked.connect(self.register_member)
button_layout.addWidget(register_member_button)
add_borrow_record_button = QtWidgets.QPushButton("Add Borrow Record")
add_borrow_record_button.clicked.connect(self.add_borrow_record)
button_layout.addWidget(add_borrow_record_button)
main_layout.addLayout(button_layout)
# Slots for button actions
def edit_book(self, item):
QtWidgets.QMessageBox.information(self, "Edit Book", f"Edit details for '{item.text()}'.")
def return_book(self, item):
QtWidgets.QMessageBox.information(self, "Return Book", f"Mark '{item.text()}' as returned.")
def register_member(self):
QtWidgets.QMessageBox.information(self, "Add Member", "Open dialog to register a new member.")
def add_borrow_record(self):
QtWidgets.QMessageBox.information(self, "Add Borrow Record", "Open dialog to add a borrow record.")
def create_menu_bar(self):
# Create the menu bar
menu_bar = self.menuBar()
# File menu
file_menu = menu_bar.addMenu("File")
import_action = QAction("Import", self)
import_action.triggered.connect(self.import_data)
file_menu.addAction(import_action)
export_action = QAction("Export", self)
export_action.triggered.connect(self.export_data)
file_menu.addAction(export_action)
file_menu.addSeparator()
exit_action = QAction("Exit", self)
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# Edit menu
edit_menu = menu_bar.addMenu("Edit")
preferences = QAction("Preferences", self)
preferences.triggered.connect(self.edit_preferences)
edit_menu.addAction(preferences)
# Help menu
help_menu = menu_bar.addMenu("Help")
about_action = QAction("About", self)
about_action.triggered.connect(self.about)
help_menu.addAction(about_action)
# Menu action slots
def edit_preferences(self):
dialog = SettingsDialog(parent=self)
if dialog.exec() == QtWidgets.QDialog.Accepted:
print("Settings were saved.")
def open_file(self):
QtWidgets.QMessageBox.information(self, "Open File", "Open an existing file.")
def import_data(self):
pass
def export_data(self):
pass
def about(self):
QtWidgets.QMessageBox.information(self, "About", "Library Dashboard v1.0\nDeveloped by You.")

2
src/ui/qmldir Normal file
View File

@ -0,0 +1,2 @@
module Example
Main 254.0 Main.qml

View File

@ -1,71 +0,0 @@
import sys
from PySide6 import QtWidgets
class SettingsDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Settings")
self.setMinimumSize(400, 300)
# 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)
# Checkbox: Enable Notifications
self.notifications_checkbox = QtWidgets.QCheckBox("Enable Notifications")
layout.addWidget(self.notifications_checkbox)
# Checkbox: Dark Mode
self.dark_mode_checkbox = QtWidgets.QCheckBox("Enable Dark Mode")
layout.addWidget(self.dark_mode_checkbox)
# Dropdown: Language Selection
self.language_label = QtWidgets.QLabel("Language:")
layout.addWidget(self.language_label)
self.language_dropdown = QtWidgets.QComboBox()
self.language_dropdown.addItems(["English", "Spanish", "French", "German"])
layout.addWidget(self.language_dropdown)
# Dropdown: Theme Selection
self.theme_label = QtWidgets.QLabel("Theme:")
layout.addWidget(self.theme_label)
self.theme_dropdown = QtWidgets.QComboBox()
self.theme_dropdown.addItems(["Light", "Dark", "System Default"])
layout.addWidget(self.theme_dropdown)
# Buttons
button_layout = QtWidgets.QHBoxLayout()
self.save_button = QtWidgets.QPushButton("Save")
self.save_button.clicked.connect(self.save_settings)
button_layout.addWidget(self.save_button)
self.cancel_button = QtWidgets.QPushButton("Cancel")
self.cancel_button.clicked.connect(self.reject)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
def save_settings(self):
# Example of how to fetch settings
notifications = self.notifications_checkbox.isChecked()
dark_mode = self.dark_mode_checkbox.isChecked()
language = self.language_dropdown.currentText()
theme = self.theme_dropdown.currentText()
print("Settings Saved:")
print(f"Notifications: {notifications}")
print(f"Dark Mode: {dark_mode}")
print(f"Language: {language}")
print(f"Theme: {theme}")
self.accept()

View File