filter's fix
This commit is contained in:
parent
ff50ea6784
commit
4b4acdcb1b
6 changed files with 144 additions and 171 deletions
|
|
@ -5,20 +5,18 @@ from views.catalog_view import CatalogView
|
||||||
class CatalogController:
|
class CatalogController:
|
||||||
def __init__(self, database, full_name, role, auth):
|
def __init__(self, database, full_name, role, auth):
|
||||||
self.database = database
|
self.database = database
|
||||||
self.model = ToyModel(database)
|
|
||||||
self.view = CatalogView(full_name, role)
|
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
self.role = role
|
self.role = role
|
||||||
|
self.model = ToyModel(database)
|
||||||
|
|
||||||
self.view.refresh_button.clicked.connect(
|
age_groups = self.model.get_age_groups()
|
||||||
self.load_data
|
suppliers = self.model.get_suppliers()
|
||||||
)
|
|
||||||
self.view.sort_button.clicked.connect(
|
self.view = CatalogView(full_name, role, age_groups, suppliers)
|
||||||
self.sort_by_price
|
|
||||||
)
|
self.view.refresh_button.clicked.connect(self.load_data)
|
||||||
self.view.logout_button.clicked.connect(
|
self.view.sort_button.clicked.connect(self.sort_by_price)
|
||||||
self.logout
|
self.view.logout_button.clicked.connect(self.logout)
|
||||||
)
|
|
||||||
|
|
||||||
self.load_data()
|
self.load_data()
|
||||||
|
|
||||||
|
|
|
||||||
5
main.py
5
main.py
|
|
@ -2,11 +2,14 @@ import sys
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
from models.database import Database
|
from models.database import Database
|
||||||
from controllers.auth_controller import AuthController
|
from controllers.auth_controller import AuthController
|
||||||
|
from config import DB_NAME, DB_USER, DB_PASSWORD
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
database = Database()
|
database = Database(dbname=DB_NAME,
|
||||||
|
user=DB_USER,
|
||||||
|
password=DB_PASSWORD)
|
||||||
controller = AuthController(database)
|
controller = AuthController(database)
|
||||||
controller.show()
|
controller.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,23 @@
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2.extras import RealDictCursor
|
|
||||||
from config import (
|
|
||||||
DB_HOST,
|
|
||||||
DB_PORT,
|
|
||||||
DB_NAME,
|
|
||||||
DB_USER,
|
|
||||||
DB_PASSWORD,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self):
|
def __init__(self, dbname, user, password, host="localhost", port=5432):
|
||||||
self.connection = psycopg2.connect(
|
self.conn = psycopg2.connect(
|
||||||
host=DB_HOST,
|
dbname=dbname, user=user, password=password, host=host, port=port
|
||||||
port=DB_PORT,
|
|
||||||
dbname=DB_NAME,
|
|
||||||
user=DB_USER,
|
|
||||||
password=DB_PASSWORD,
|
|
||||||
cursor_factory=RealDictCursor,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def fetch_all(self, query: str, params: tuple = ()):
|
def query(self, sql, params=None):
|
||||||
with self.connection.cursor() as cursor:
|
with self.conn.cursor() as cur:
|
||||||
cursor.execute(query, params)
|
cur.execute(sql, params or ())
|
||||||
return cursor.fetchall()
|
if cur.description:
|
||||||
|
return cur.fetchall()
|
||||||
|
return None
|
||||||
|
|
||||||
def fetch_one(self, query: str, params: tuple = ()):
|
def fetch_one(self, sql, params=None):
|
||||||
with self.connection.cursor() as cursor:
|
with self.conn.cursor() as cur:
|
||||||
cursor.execute(query, params)
|
cur.execute(sql, params or ())
|
||||||
return cursor.fetchone()
|
return cur.fetchone()
|
||||||
|
|
||||||
def execute(self, query: str, params: tuple = ()):
|
def commit(self):
|
||||||
with self.connection.cursor() as cursor:
|
self.conn.commit()
|
||||||
cursor.execute(query, params)
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,47 @@
|
||||||
from models.database import Database
|
|
||||||
|
|
||||||
|
|
||||||
class ToyModel:
|
class ToyModel:
|
||||||
def __init__(self, database: Database):
|
def __init__(self, database: "Database"):
|
||||||
self.database = database
|
self.db = database
|
||||||
|
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
query = """
|
sql = """
|
||||||
SELECT t.toy_id,
|
SELECT t.toy_name, c.category_name, m.manufacturer_name,
|
||||||
t.toy_name,
|
STRING_AGG(DISTINCT ag.age_label, ', ') AS ages,
|
||||||
c.category_name,
|
STRING_AGG(DISTINCT s.supplier_name, ', ') AS suppliers,
|
||||||
m.manufacturer_name,
|
t.price, t.discount, st.quantity
|
||||||
t.price,
|
|
||||||
t.discount,
|
|
||||||
COALESCE(s.quantity, 0) AS quantity,
|
|
||||||
STRING_AGG(DISTINCT a.age_label, ', ') AS ages,
|
|
||||||
STRING_AGG(DISTINCT sup.supplier_name, ', ') AS suppliers
|
|
||||||
FROM toys t
|
FROM toys t
|
||||||
JOIN categories c ON t.category_id = c.category_id
|
JOIN categories c ON t.category_id = c.category_id
|
||||||
JOIN manufacturers m ON t.manufacturer_id = m.manufacturer_id
|
JOIN manufacturers m ON t.manufacturer_id = m.manufacturer_id
|
||||||
LEFT JOIN stock s ON t.toy_id = s.toy_id
|
LEFT JOIN toy_age_groups tag ON t.toy_id = tag.toy_id
|
||||||
LEFT JOIN toy_age_groups ta ON t.toy_id = ta.toy_id
|
LEFT JOIN age_groups ag ON tag.age_group_id = ag.age_group_id
|
||||||
LEFT JOIN age_groups a ON ta.age_group_id = a.age_group_id
|
|
||||||
LEFT JOIN toy_suppliers ts ON t.toy_id = ts.toy_id
|
LEFT JOIN toy_suppliers ts ON t.toy_id = ts.toy_id
|
||||||
LEFT JOIN suppliers sup ON ts.supplier_id = sup.supplier_id
|
LEFT JOIN suppliers s ON ts.supplier_id = s.supplier_id
|
||||||
GROUP BY t.toy_id, c.category_name,
|
LEFT JOIN stock st ON t.toy_id = st.toy_id
|
||||||
m.manufacturer_name, s.quantity
|
GROUP BY t.toy_id, c.category_name, m.manufacturer_name, t.price, t.discount, st.quantity
|
||||||
ORDER BY t.toy_name
|
ORDER BY t.toy_id
|
||||||
"""
|
"""
|
||||||
return self.database.fetch_all(query)
|
rows = self.db.query(sql)
|
||||||
|
toys = []
|
||||||
|
for r in rows:
|
||||||
|
toys.append({
|
||||||
|
"toy_name": r[0],
|
||||||
|
"category_name": r[1],
|
||||||
|
"manufacturer_name": r[2],
|
||||||
|
"ages": r[3] or "",
|
||||||
|
"suppliers": r[4] or "",
|
||||||
|
"price": float(r[5]),
|
||||||
|
"discount": int(r[6]),
|
||||||
|
"quantity": int(r[7] or 0),
|
||||||
|
})
|
||||||
|
return toys
|
||||||
|
|
||||||
def search_by_age(self, age: str):
|
def sort_by_price(self, ascending=True):
|
||||||
query = """
|
toys = self.get_all()
|
||||||
SELECT *
|
return sorted(toys, key=lambda x: x["price"], reverse=not ascending)
|
||||||
FROM (
|
|
||||||
SELECT t.toy_id,
|
|
||||||
t.toy_name,
|
|
||||||
c.category_name,
|
|
||||||
m.manufacturer_name,
|
|
||||||
t.price,
|
|
||||||
t.discount,
|
|
||||||
COALESCE(s.quantity, 0) AS quantity,
|
|
||||||
STRING_AGG(DISTINCT a.age_label, ', ') AS ages,
|
|
||||||
STRING_AGG(DISTINCT sup.supplier_name, ', ') AS suppliers
|
|
||||||
FROM toys t
|
|
||||||
JOIN categories c ON t.category_id = c.category_id
|
|
||||||
JOIN manufacturers m ON t.manufacturer_id = m.manufacturer_id
|
|
||||||
LEFT JOIN stock s ON t.toy_id = s.toy_id
|
|
||||||
LEFT JOIN toy_age_groups ta ON t.toy_id = ta.toy_id
|
|
||||||
LEFT JOIN age_groups a ON ta.age_group_id = a.age_group_id
|
|
||||||
LEFT JOIN toy_suppliers ts ON t.toy_id = ts.toy_id
|
|
||||||
LEFT JOIN suppliers sup ON ts.supplier_id = sup.supplier_id
|
|
||||||
GROUP BY t.toy_id, c.category_name,
|
|
||||||
m.manufacturer_name, s.quantity
|
|
||||||
) sub
|
|
||||||
WHERE ages ILIKE %s
|
|
||||||
"""
|
|
||||||
return self.database.fetch_all(query, (f"%{age}%",))
|
|
||||||
|
|
||||||
def sort_by_price(self):
|
def get_age_groups(self):
|
||||||
query = """
|
rows = self.db.query("SELECT age_label FROM age_groups ORDER BY age_group_id")
|
||||||
SELECT *
|
return [r[0] for r in rows]
|
||||||
FROM (
|
|
||||||
SELECT t.toy_id,
|
def get_suppliers(self):
|
||||||
t.toy_name,
|
rows = self.db.query("SELECT supplier_name FROM suppliers ORDER BY supplier_id")
|
||||||
c.category_name,
|
return [r[0] for r in rows]
|
||||||
m.manufacturer_name,
|
|
||||||
t.price,
|
|
||||||
t.discount,
|
|
||||||
COALESCE(s.quantity, 0) AS quantity,
|
|
||||||
STRING_AGG(DISTINCT a.age_label, ', ') AS ages,
|
|
||||||
STRING_AGG(DISTINCT sup.supplier_name, ', ') AS suppliers
|
|
||||||
FROM toys t
|
|
||||||
JOIN categories c ON t.category_id = c.category_id
|
|
||||||
JOIN manufacturers m ON t.manufacturer_id = m.manufacturer_id
|
|
||||||
LEFT JOIN stock s ON t.toy_id = s.toy_id
|
|
||||||
LEFT JOIN toy_age_groups ta ON t.toy_id = ta.toy_id
|
|
||||||
LEFT JOIN age_groups a ON ta.age_group_id = a.age_group_id
|
|
||||||
LEFT JOIN toy_suppliers ts ON t.toy_id = ts.toy_id
|
|
||||||
LEFT JOIN suppliers sup ON ts.supplier_id = sup.supplier_id
|
|
||||||
GROUP BY t.toy_id, c.category_name,
|
|
||||||
m.manufacturer_name, s.quantity
|
|
||||||
) sub
|
|
||||||
ORDER BY price
|
|
||||||
"""
|
|
||||||
return self.database.fetch_all(query)
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
from models.database import Database
|
from models.database import Database
|
||||||
|
|
||||||
|
|
||||||
class UserModel:
|
class UserModel:
|
||||||
def __init__(self, database: Database):
|
def __init__(self, database):
|
||||||
self.database = database
|
self.db = database
|
||||||
|
|
||||||
def authenticate(self, login: str, password: str):
|
def authenticate(self, login, password):
|
||||||
query = """
|
sql = """
|
||||||
SELECT u.user_id,
|
SELECT u.login, u.password, u.full_name, r.role_name
|
||||||
u.full_name,
|
|
||||||
r.role_name
|
|
||||||
FROM users u
|
FROM users u
|
||||||
JOIN roles r ON u.role_id = r.role_id
|
JOIN roles r ON u.role_id = r.role_id
|
||||||
WHERE u.login = %s AND u.password = %s
|
WHERE u.login = %s AND u.password = %s
|
||||||
"""
|
"""
|
||||||
return self.database.fetch_one(query, (login, password))
|
row = self.db.fetch_one(sql, (login, password))
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"login": row[0],
|
||||||
|
"password": row[1],
|
||||||
|
"full_name": row[2],
|
||||||
|
"role_name": row[3],
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,101 @@
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QWidget,
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,
|
||||||
QVBoxLayout,
|
QTableWidget, QTableWidgetItem, QComboBox
|
||||||
QPushButton,
|
|
||||||
QLabel,
|
|
||||||
QTableWidget,
|
|
||||||
QTableWidgetItem,
|
|
||||||
)
|
)
|
||||||
from PyQt6.QtGui import QColor
|
from PyQt6.QtGui import QColor
|
||||||
from config import DISCOUNT_COLOR, OUT_OF_STOCK_COLOR
|
from config import DISCOUNT_COLOR, OUT_OF_STOCK_COLOR
|
||||||
|
|
||||||
|
|
||||||
class CatalogView(QWidget):
|
class CatalogView(QWidget):
|
||||||
def __init__(self, full_name: str, role: str):
|
def __init__(self, full_name, role, age_groups, suppliers):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.user_role = role
|
||||||
self.setWindowTitle("Каталог игрушек")
|
self.setWindowTitle("Каталог игрушек")
|
||||||
|
self.setFixedSize(860, 500)
|
||||||
|
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
|
self.user_label = QLabel(f"{full_name} ({role})")
|
||||||
|
|
||||||
self.user_label = QLabel(
|
# Кнопки
|
||||||
f"{full_name} ({role})"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.refresh_button = QPushButton("Обновить")
|
self.refresh_button = QPushButton("Обновить")
|
||||||
self.sort_button = QPushButton("Сортировать по цене")
|
self.sort_button = QPushButton("Сортировать по цене")
|
||||||
self.logout_button = QPushButton("Выйти")
|
self.logout_button = QPushButton("Выйти")
|
||||||
|
|
||||||
|
# Фильтры
|
||||||
|
self.filter_age_combo = QComboBox()
|
||||||
|
self.filter_supplier_combo = QComboBox()
|
||||||
|
|
||||||
|
self.filter_age_combo.addItem("Все возрастные категории")
|
||||||
|
for age in age_groups:
|
||||||
|
self.filter_age_combo.addItem(age)
|
||||||
|
|
||||||
|
self.filter_supplier_combo.addItem("Все поставщики")
|
||||||
|
for sup in suppliers:
|
||||||
|
self.filter_supplier_combo.addItem(sup)
|
||||||
|
|
||||||
|
# Включаем фильтры по роли
|
||||||
|
self.filter_age_combo.setEnabled(role in ("Покупатель", "Сотрудник", "Администратор"))
|
||||||
|
self.filter_supplier_combo.setEnabled(role in ("Сотрудник", "Администратор"))
|
||||||
|
|
||||||
|
# Панель фильтров и кнопок
|
||||||
|
top_layout = QHBoxLayout()
|
||||||
|
top_layout.addWidget(self.refresh_button)
|
||||||
|
top_layout.addWidget(self.sort_button)
|
||||||
|
top_layout.addWidget(self.filter_age_combo)
|
||||||
|
top_layout.addWidget(self.filter_supplier_combo)
|
||||||
|
top_layout.addWidget(self.logout_button)
|
||||||
|
|
||||||
self.table = QTableWidget()
|
self.table = QTableWidget()
|
||||||
|
|
||||||
self.layout.addWidget(self.user_label)
|
self.layout.addWidget(self.user_label)
|
||||||
self.layout.addWidget(self.refresh_button)
|
self.layout.addLayout(top_layout)
|
||||||
self.layout.addWidget(self.sort_button)
|
|
||||||
self.layout.addWidget(self.table)
|
self.layout.addWidget(self.table)
|
||||||
self.layout.addWidget(self.logout_button)
|
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
self.setFixedSize(860, 500)
|
self.filter_age_combo.currentTextChanged.connect(self.apply_filters)
|
||||||
|
self.filter_supplier_combo.currentTextChanged.connect(self.apply_filters)
|
||||||
|
|
||||||
def load_data(self, toys: list):
|
self.all_toys = []
|
||||||
|
|
||||||
|
def load_data(self, toys):
|
||||||
|
self.all_toys = toys
|
||||||
|
self.apply_filters()
|
||||||
|
|
||||||
|
def apply_filters(self):
|
||||||
|
filtered = self.all_toys
|
||||||
|
age_filter = self.filter_age_combo.currentText()
|
||||||
|
supplier_filter = self.filter_supplier_combo.currentText()
|
||||||
|
|
||||||
|
if self.user_role in ("Покупатель", "Сотрудник", "Администратор"):
|
||||||
|
if age_filter != "Все возрастные категории":
|
||||||
|
filtered = [t for t in filtered if age_filter in t["ages"].split(", ")]
|
||||||
|
|
||||||
|
if self.user_role in ("Сотрудник", "Администратор"):
|
||||||
|
if supplier_filter != "Все поставщики":
|
||||||
|
filtered = [t for t in filtered if supplier_filter in t["suppliers"].split(", ")]
|
||||||
|
|
||||||
|
self._populate_table(filtered)
|
||||||
|
|
||||||
|
def _populate_table(self, toys):
|
||||||
headers = [
|
headers = [
|
||||||
"Название",
|
"Название", "Категория", "Производитель",
|
||||||
"Категория",
|
"Возраст", "Поставщик", "Цена", "Скидка", "Остаток"
|
||||||
"Производитель",
|
|
||||||
"Возраст",
|
|
||||||
"Поставщик",
|
|
||||||
"Цена",
|
|
||||||
"Скидка",
|
|
||||||
"Остаток",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
self.table.setColumnCount(len(headers))
|
self.table.setColumnCount(len(headers))
|
||||||
self.table.setHorizontalHeaderLabels(headers)
|
self.table.setHorizontalHeaderLabels(headers)
|
||||||
self.table.setRowCount(len(toys))
|
self.table.setRowCount(len(toys))
|
||||||
|
|
||||||
for row, toy in enumerate(toys):
|
for row, toy in enumerate(toys):
|
||||||
values = [
|
values = [
|
||||||
toy["toy_name"],
|
toy["toy_name"], toy["category_name"], toy["manufacturer_name"],
|
||||||
toy["category_name"],
|
toy["ages"], toy["suppliers"], str(toy["price"]),
|
||||||
toy["manufacturer_name"],
|
str(toy["discount"]), str(toy["quantity"])
|
||||||
toy["ages"],
|
|
||||||
toy["suppliers"],
|
|
||||||
str(toy["price"]),
|
|
||||||
str(toy["discount"]),
|
|
||||||
str(toy["quantity"]),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for col, value in enumerate(values):
|
for col, value in enumerate(values):
|
||||||
item = QTableWidgetItem(value)
|
self.table.setItem(row, col, QTableWidgetItem(value))
|
||||||
self.table.setItem(row, col, item)
|
|
||||||
|
|
||||||
if toy["discount"] >= 25:
|
if toy["discount"] >= 25:
|
||||||
for col in range(len(headers)):
|
for col in range(len(headers)):
|
||||||
self.table.item(row, col).setBackground(
|
self.table.item(row, col).setBackground(QColor(DISCOUNT_COLOR))
|
||||||
QColor(DISCOUNT_COLOR)
|
|
||||||
)
|
|
||||||
|
|
||||||
if toy["quantity"] == 0:
|
if toy["quantity"] == 0:
|
||||||
for col in range(len(headers)):
|
for col in range(len(headers)):
|
||||||
self.table.item(row, col).setBackground(
|
self.table.item(row, col).setBackground(QColor(OUT_OF_STOCK_COLOR))
|
||||||
QColor(OUT_OF_STOCK_COLOR)
|
|
||||||
)
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue