Final Update: second variant complete

This commit is contained in:
Daniel 2025-11-26 12:56:14 +03:00
parent 703adc3326
commit b92a91ab37
8 changed files with 2939 additions and 62 deletions

1302
control1-2.py Normal file

File diff suppressed because it is too large Load diff

902
control2-2.py Normal file
View file

@ -0,0 +1,902 @@
import sys
import sqlite3
from datetime import datetime, date
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QTabWidget, QTableWidget, QTableWidgetItem,
QPushButton, QLabel, QLineEdit, QComboBox, QDateEdit,
QTextEdit, QMessageBox, QHeaderView, QGroupBox,
QFormLayout, QSpinBox, QCheckBox, QTimeEdit, QProgressBar)
from PyQt6.QtCore import Qt, QDate
from PyQt6.QtGui import QFont, QPalette, QColor
class FitnessApp(QMainWindow):
def __init__(self):
super().__init__()
self.initDB()
self.initUI()
def initDB(self):
"""Инициализация базы данных"""
self.conn = sqlite3.connect('fitness.db')
self.cursor = self.conn.cursor()
# Создание таблиц
self.create_tables()
# Заполнение тестовыми данными
self.insert_sample_data()
def create_tables(self):
"""Создание таблиц базы данных"""
tables = [
"""
CREATE TABLE IF NOT EXISTS Users (
userID INTEGER PRIMARY KEY,
fio TEXT NOT NULL,
phone TEXT,
email TEXT,
login TEXT UNIQUE,
password TEXT,
userType TEXT,
specialization TEXT,
birthDate DATE
)
""",
"""
CREATE TABLE IF NOT EXISTS Memberships (
membershipID INTEGER PRIMARY KEY,
clientID INTEGER,
membershipType TEXT,
startDate DATE,
endDate DATE,
visitsTotal INTEGER,
visitsUsed INTEGER,
zones TEXT,
membershipStatus TEXT,
cost REAL,
adminID INTEGER,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (adminID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS Visits (
visitID INTEGER PRIMARY KEY,
clientID INTEGER,
visitDate DATE,
checkInTime TIME,
checkOutTime TIME,
zone TEXT,
membershipID INTEGER,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (membershipID) REFERENCES Memberships(membershipID)
)
""",
"""
CREATE TABLE IF NOT EXISTS GroupClasses (
classID INTEGER PRIMARY KEY,
className TEXT,
trainerID INTEGER,
classDate DATE,
startTime TIME,
endTime TIME,
hall TEXT,
maxParticipants INTEGER,
enrolledParticipants INTEGER,
classStatus TEXT,
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS PersonalTraining (
trainingID INTEGER PRIMARY KEY,
clientID INTEGER,
trainerID INTEGER,
trainingDate DATE,
startTime TIME,
endTime TIME,
exercises TEXT,
notes TEXT,
progressMetrics TEXT,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS ClassRegistrations (
registrationID INTEGER PRIMARY KEY,
classID INTEGER,
clientID INTEGER,
registrationDate DATE,
status TEXT,
FOREIGN KEY (classID) REFERENCES GroupClasses(classID),
FOREIGN KEY (clientID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS EquipmentRequests (
requestID INTEGER PRIMARY KEY,
trainerID INTEGER,
equipment TEXT,
quantity INTEGER,
requestDate DATE,
status TEXT,
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS ShiftSwaps (
swapID INTEGER PRIMARY KEY,
trainerID1 INTEGER,
trainerID2 INTEGER,
shiftDate DATE,
status TEXT,
FOREIGN KEY (trainerID1) REFERENCES Users(userID),
FOREIGN KEY (trainerID2) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS Exercises (
exerciseID INTEGER PRIMARY KEY,
name TEXT,
muscleGroup TEXT,
difficulty TEXT,
description TEXT
)
"""
]
for table in tables:
self.cursor.execute(table)
self.conn.commit()
def insert_sample_data(self):
"""Вставка тестовых данных"""
# Проверяем, есть ли уже данные
self.cursor.execute("SELECT COUNT(*) FROM Users")
if self.cursor.fetchone()[0] > 0:
return
users = [
(1, 'Сидорова Марина Петровна', '89219014567', 'director@fitness.ru', 'director1', 'pass1', 'Директор', '', '1980-05-15'),
(2, 'Романова Анна Сергеевна', '89210125678', 'admin1@fitness.ru', 'admin1', 'pass2', 'Администратор', '', '1992-08-22'),
(4, 'Яковлева Елена Викторовна', '89211236789', 'admin2@fitness.ru', 'admin2', 'pass3', 'Администратор', '', '1988-11-10'),
(7, 'Петров Дмитрий Александрович', '89212347890', 'petrov@fitness.ru', 'trainer1', 'pass4', 'Тренер', 'Силовые тренировки', '1985-03-18'),
(9, 'Смирнова Ольга Игоревна', '89213458901', 'smirnova@fitness.ru', 'trainer2', 'pass5', 'Тренер', 'Йога, Пилатес', '1990-07-25'),
(11, 'Козлов Сергей Николаевич', '89214569012', 'kozlov@fitness.ru', 'trainer3', 'pass6', 'Тренер', 'Плавание', '1987-12-05'),
(16, 'Федорова Екатерина Дмитриевна', '89161112236', 'fedorova@mail.ru', 'client1', 'pass7', 'Клиент', '', '1995-04-12'),
(21, 'Михайлов Алексей Владимирович', '89162223347', 'mikhailov@gmail.com', 'client2', 'pass8', 'Клиент', '', '1988-09-30'),
(26, 'Новикова Ирина Сергеевна', '89163334458', 'novikova@yandex.ru', 'client3', 'pass9', 'Клиент', '', '1992-06-18'),
(30, 'Соколов Игорь Петрович', '89164445569', 'sokolov@mail.ru', 'client4', 'pass10', 'Клиент', '', '1983-02-28'),
(34, 'Павлова Мария Александровна', '89165556670', 'pavlova@gmail.com', 'client5', 'pass11', 'Клиент', '', '1997-11-07')
]
memberships = [
(1, 16, 'Месячный безлимит', '2024-06-01', '2024-06-30', 999, 42, 'Зал, Бассейн, Групповые', 'Активен', 5000.00, 2),
(2, 21, '12 посещений', '2024-06-05', '2024-09-05', 12, 8, 'Зал', 'Активен', 4000.00, 2),
(3, 26, 'Годовой VIP', '2024-01-10', '2025-01-10', 999, 156, 'Все зоны', 'Активен', 45000.00, 4),
(4, 30, 'Разовое посещение', '2024-06-15', '2024-06-15', 1, 1, 'Зал', 'Завершен', 500.00, 2),
(5, 34, 'Квартальный', '2024-06-01', '2024-08-31', 999, 15, 'Зал, Групповые', 'Активен', 12000.00, 4)
]
visits = [
(1, 16, '2024-06-15', '08:30', '10:15', 'Тренажерный зал', 1),
(2, 21, '2024-06-15', '09:00', '10:30', 'Тренажерный зал', 2),
(3, 26, '2024-06-15', '07:00', '08:30', 'Бассейн', 3),
(4, 16, '2024-06-15', '18:00', '19:45', 'Групповое занятие', 1),
(5, 34, '2024-06-15', '19:00', '20:30', 'Групповое занятие', 5)
]
group_classes = [
(1, 'Йога для начинающих', 9, '2024-06-16', '10:00', '11:00', 'Зал 2', 15, 12, 'Запланировано'),
(2, 'Силовая аэробика', 7, '2024-06-16', '18:00', '19:00', 'Зал 1', 20, 18, 'Запланировано'),
(3, 'Пилатес', 9, '2024-06-17', '11:00', '12:00', 'Зал 2', 12, 12, 'Группа заполнена'),
(4, 'Аквааэробика', 11, '2024-06-17', '15:00', '16:00', 'Бассейн', 10, 7, 'Запланировано'),
(5, 'Бокс', 7, '2024-06-18', '19:00', '20:30', 'Зал 3', 8, 5, 'Запланировано')
]
personal_training = [
(1, 26, 7, '2024-06-14', '16:00', '17:00', 'Жим лежа, Приседания, Тяга блока', 'Хорошая техника', 'Жим 80кг x 8'),
(2, 16, 9, '2024-06-13', '10:00', '11:00', 'Асаны йоги, Растяжка', 'Улучшилась гибкость', ''),
(3, 21, 7, '2024-06-12', '14:00', '15:00', 'Становая тяга, Жим гантелей', 'Нужно работать над техникой', 'Становая 60кг x 6')
]
exercises = [
(1, 'Жим лежа', 'Грудь', 'Средний', 'Упражнение для развития грудных мышц'),
(2, 'Приседания', 'Ноги', 'Начальный', 'Базовое упражнение для ног'),
(3, 'Тяга блока', 'Спина', 'Средний', 'Упражнение для развития широчайших мышц'),
(4, 'Асаны йоги', 'Все тело', 'Начальный', 'Позы для развития гибкости'),
(5, 'Становая тяга', 'Спина, Ноги', 'Продвинутый', 'Базовое упражнение для спины и ног')
]
# Вставка данных
self.cursor.executemany("INSERT INTO Users VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", users)
self.cursor.executemany("INSERT INTO Memberships VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", memberships)
self.cursor.executemany("INSERT INTO Visits VALUES (?, ?, ?, ?, ?, ?, ?)", visits)
self.cursor.executemany("INSERT INTO GroupClasses VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", group_classes)
self.cursor.executemany("INSERT INTO PersonalTraining VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", personal_training)
self.cursor.executemany("INSERT INTO Exercises VALUES (?, ?, ?, ?, ?)", exercises)
self.conn.commit()
def initUI(self):
"""Инициализация пользовательского интерфейса"""
self.setWindowTitle('Фитнес-клуб - Система управления (Вариант 2)')
self.setGeometry(100, 100, 1200, 800)
# Центральный виджет с вкладками для разных ролей
self.tabs = QTabWidget()
# Вкладка для администратора
self.admin_tab = QWidget()
self.init_admin_tab()
self.tabs.addTab(self.admin_tab, "Администратор")
# Вкладка для тренера
self.trainer_tab = QWidget()
self.init_trainer_tab()
self.tabs.addTab(self.trainer_tab, "Тренер")
# Вкладка для директора
self.director_tab = QWidget()
self.init_director_tab()
self.tabs.addTab(self.director_tab, "Директор")
self.setCentralWidget(self.tabs)
def init_admin_tab(self):
"""Инициализация вкладки администратора"""
layout = QVBoxLayout()
# Группа управления расписанием
schedule_group = QGroupBox("Управление расписанием групповых занятий")
schedule_layout = QVBoxLayout()
# Таблица групповых занятий
self.classes_table = QTableWidget()
self.classes_table.setColumnCount(10)
self.classes_table.setHorizontalHeaderLabels([
'ID', 'Название', 'Тренер', 'Дата', 'Время начала',
'Время окончания', 'Зал', 'Макс. участников', 'Записано', 'Статус'
])
self.classes_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
schedule_layout.addWidget(self.classes_table)
# Кнопки управления
btn_layout = QHBoxLayout()
self.add_class_btn = QPushButton("Добавить занятие")
self.edit_class_btn = QPushButton("Редактировать")
self.delete_class_btn = QPushButton("Удалить")
self.assign_trainer_btn = QPushButton("Назначить тренера")
btn_layout.addWidget(self.add_class_btn)
btn_layout.addWidget(self.edit_class_btn)
btn_layout.addWidget(self.delete_class_btn)
btn_layout.addWidget(self.assign_trainer_btn)
schedule_layout.addLayout(btn_layout)
schedule_group.setLayout(schedule_layout)
layout.addWidget(schedule_group)
# Группа управления абонементами
membership_group = QGroupBox("Управление абонементами")
membership_layout = QVBoxLayout()
self.memberships_table = QTableWidget()
self.memberships_table.setColumnCount(8)
self.memberships_table.setHorizontalHeaderLabels([
'ID', 'Клиент', 'Тип', 'Начало', 'Окончание',
'Использовано/Всего', 'Статус', 'Стоимость'
])
self.memberships_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
membership_layout.addWidget(self.memberships_table)
# Кнопки для управления абонементами
membership_btn_layout = QHBoxLayout()
self.change_price_btn = QPushButton("Изменить стоимость")
self.freeze_membership_btn = QPushButton("Заморозить абонемент")
self.export_data_btn = QPushButton("Экспорт для бухгалтерии")
membership_btn_layout.addWidget(self.change_price_btn)
membership_btn_layout.addWidget(self.freeze_membership_btn)
membership_btn_layout.addWidget(self.export_data_btn)
membership_layout.addLayout(membership_btn_layout)
membership_group.setLayout(membership_layout)
layout.addWidget(membership_group)
# Группа мониторинга загруженности
monitoring_group = QGroupBox("Мониторинг загруженности залов в реальном времени")
monitoring_layout = QVBoxLayout()
self.hall_occupancy_table = QTableWidget()
self.hall_occupancy_table.setColumnCount(3)
self.hall_occupancy_table.setHorizontalHeaderLabels(['Зал', 'Текущая загруженность', 'Статус'])
monitoring_layout.addWidget(self.hall_occupancy_table)
monitoring_group.setLayout(monitoring_layout)
layout.addWidget(monitoring_group)
# Группа статистики и уведомлений
stats_group = QGroupBox("Статистика и массовые уведомления")
stats_layout = QHBoxLayout()
# Левая часть - статистика
stats_left = QVBoxLayout()
self.attendance_stats = QTextEdit()
self.attendance_stats.setMaximumHeight(150)
stats_left.addWidget(QLabel("Статистика посещаемости:"))
stats_left.addWidget(self.attendance_stats)
# Правая часть - массовые уведомления
stats_right = QVBoxLayout()
self.mass_notification_text = QTextEdit()
self.mass_notification_text.setMaximumHeight(100)
self.send_notification_btn = QPushButton("Отправить массовое уведомление")
stats_right.addWidget(QLabel("Массовое уведомление:"))
stats_right.addWidget(self.mass_notification_text)
stats_right.addWidget(self.send_notification_btn)
stats_layout.addLayout(stats_left)
stats_layout.addLayout(stats_right)
stats_group.setLayout(stats_layout)
layout.addWidget(stats_group)
# Загрузка данных
self.load_classes_data()
self.load_memberships_data()
self.load_hall_occupancy()
self.load_attendance_stats()
# Подключение сигналов
self.add_class_btn.clicked.connect(self.add_class)
self.assign_trainer_btn.clicked.connect(self.assign_trainer)
self.change_price_btn.clicked.connect(self.change_membership_price)
self.freeze_membership_btn.clicked.connect(self.freeze_membership)
self.export_data_btn.clicked.connect(self.export_accounting_data)
self.send_notification_btn.clicked.connect(self.send_mass_notification)
self.admin_tab.setLayout(layout)
def init_trainer_tab(self):
"""Инициализация вкладки тренера"""
layout = QVBoxLayout()
# Группа программ тренировок
programs_group = QGroupBox("Индивидуальные программы тренировок")
programs_layout = QVBoxLayout()
self.programs_table = QTableWidget()
self.programs_table.setColumnCount(6)
self.programs_table.setHorizontalHeaderLabels([
'ID', 'Клиент', 'Дата', 'Упражнения', 'Заметки', 'Прогресс'
])
self.programs_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
programs_layout.addWidget(self.programs_table)
programs_btn_layout = QHBoxLayout()
self.add_program_btn = QPushButton("Создать программу")
self.edit_program_btn = QPushButton("Редактировать")
self.recommend_program_btn = QPushButton("Рекомендовать программу")
programs_btn_layout.addWidget(self.add_program_btn)
programs_btn_layout.addWidget(self.edit_program_btn)
programs_btn_layout.addWidget(self.recommend_program_btn)
programs_layout.addLayout(programs_btn_layout)
programs_group.setLayout(programs_layout)
layout.addWidget(programs_group)
# Группа базы упражнений
exercises_group = QGroupBox("База упражнений")
exercises_layout = QVBoxLayout()
self.exercises_table = QTableWidget()
self.exercises_table.setColumnCount(5)
self.exercises_table.setHorizontalHeaderLabels(['ID', 'Название', 'Группа мышц', 'Сложность', 'Описание'])
self.exercises_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
exercises_layout.addWidget(self.exercises_table)
exercises_btn_layout = QHBoxLayout()
self.add_exercise_btn = QPushButton("Добавить упражнение")
self.edit_exercise_btn = QPushButton("Редактировать")
exercises_btn_layout.addWidget(self.add_exercise_btn)
exercises_btn_layout.addWidget(self.edit_exercise_btn)
exercises_layout.addLayout(exercises_btn_layout)
exercises_group.setLayout(exercises_layout)
layout.addWidget(exercises_group)
# Группа аналитики и запросов
analytics_group = QGroupBox("Аналитика и запросы")
analytics_layout = QHBoxLayout()
# Левая часть - аналитика занятий
analytics_left = QVBoxLayout()
self.class_attendance_stats = QTextEdit()
self.class_attendance_stats.setMaximumHeight(120)
analytics_left.addWidget(QLabel("Аналитика посещаемости занятий:"))
analytics_left.addWidget(self.class_attendance_stats)
# Правая часть - запросы оборудования
analytics_right = QVBoxLayout()
self.equipment_table = QTableWidget()
self.equipment_table.setColumnCount(5)
self.equipment_table.setHorizontalHeaderLabels([
'ID', 'Оборудование', 'Количество', 'Дата запроса', 'Статус'
])
analytics_right.addWidget(QLabel("Запросы оборудования:"))
analytics_right.addWidget(self.equipment_table)
equipment_btn_layout = QHBoxLayout()
self.request_equipment_btn = QPushButton("Запросить оборудование")
equipment_btn_layout.addWidget(self.request_equipment_btn)
analytics_right.addLayout(equipment_btn_layout)
analytics_layout.addLayout(analytics_left)
analytics_layout.addLayout(analytics_right)
analytics_group.setLayout(analytics_layout)
layout.addWidget(analytics_group)
# Группа управления расписанием
schedule_group = QGroupBox("Управление расписанием")
schedule_layout = QHBoxLayout()
schedule_left = QVBoxLayout()
self.trainer_schedule_table = QTableWidget()
self.trainer_schedule_table.setColumnCount(5)
self.trainer_schedule_table.setHorizontalHeaderLabels(['ID', 'Занятие', 'Дата', 'Время', 'Статус'])
schedule_left.addWidget(QLabel("Мое расписание:"))
schedule_left.addWidget(self.trainer_schedule_table)
schedule_right = QVBoxLayout()
self.block_time_btn = QPushButton("Заблокировать время")
self.swap_shift_btn = QPushButton("Обменяться сменой")
self.view_bonuses_btn = QPushButton("Просмотреть бонусы")
schedule_right.addWidget(self.block_time_btn)
schedule_right.addWidget(self.swap_shift_btn)
schedule_right.addWidget(self.view_bonuses_btn)
schedule_right.addStretch()
schedule_layout.addLayout(schedule_left)
schedule_layout.addLayout(schedule_right)
schedule_group.setLayout(schedule_layout)
layout.addWidget(schedule_group)
# Загрузка данных
self.load_programs_data()
self.load_exercises_data()
self.load_equipment_data()
self.load_trainer_schedule()
self.load_class_attendance_stats()
# Подключение сигналов
self.add_program_btn.clicked.connect(self.add_training_program)
self.add_exercise_btn.clicked.connect(self.add_exercise)
self.request_equipment_btn.clicked.connect(self.request_equipment)
self.block_time_btn.clicked.connect(self.block_time)
self.swap_shift_btn.clicked.connect(self.swap_shift)
self.view_bonuses_btn.clicked.connect(self.view_bonuses)
self.trainer_tab.setLayout(layout)
def init_director_tab(self):
"""Инициализация вкладки директора"""
layout = QVBoxLayout()
# Общая статистика
overall_stats_group = QGroupBox("Общая статистика клуба")
overall_layout = QHBoxLayout()
# Левая часть - ключевые показатели
metrics_layout = QFormLayout()
self.total_members_label = QLabel("0")
self.active_members_label = QLabel("0")
self.monthly_revenue_label = QLabel("0 руб.")
self.attendance_rate_label = QLabel("0%")
self.avg_rating_label = QLabel("0.0")
self.employee_count_label = QLabel("0")
# Стилизация метрик
for label in [self.total_members_label, self.active_members_label,
self.monthly_revenue_label, self.attendance_rate_label,
self.avg_rating_label, self.employee_count_label]:
label.setStyleSheet("font-weight: bold; font-size: 14px; color: #2E86AB;")
metrics_layout.addRow("Всего клиентов:", self.total_members_label)
metrics_layout.addRow("Активных абонементов:", self.active_members_label)
metrics_layout.addRow("Доход за месяц:", self.monthly_revenue_label)
metrics_layout.addRow("Средняя посещаемость:", self.attendance_rate_label)
metrics_layout.addRow("Средняя оценка:", self.avg_rating_label)
metrics_layout.addRow("Сотрудников:", self.employee_count_label)
overall_layout.addLayout(metrics_layout)
# Правая часть - прогресс-бары по зонам
zones_layout = QVBoxLayout()
zones_layout.addWidget(QLabel("Загруженность зон:"))
self.gym_usage_bar = QProgressBar()
self.pool_usage_bar = QProgressBar()
self.group_usage_bar = QProgressBar()
self.gym_usage_bar.setFormat("Тренажерный зал: %p%")
self.pool_usage_bar.setFormat("Бассейн: %p%")
self.group_usage_bar.setFormat("Групповые занятия: %p%")
zones_layout.addWidget(self.gym_usage_bar)
zones_layout.addWidget(self.pool_usage_bar)
zones_layout.addWidget(self.group_usage_bar)
overall_layout.addLayout(zones_layout)
overall_stats_group.setLayout(overall_layout)
layout.addWidget(overall_stats_group)
# Эффективность тренеров
trainers_group = QGroupBox("Эффективность тренеров")
trainers_layout = QVBoxLayout()
self.trainers_table = QTableWidget()
self.trainers_table.setColumnCount(6)
self.trainers_table.setHorizontalHeaderLabels([
'Тренер', 'Групповые занятия', 'Персональные тренировки',
'Средняя оценка', 'Доход', 'Бонусы'
])
self.trainers_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
trainers_layout.addWidget(self.trainers_table)
trainers_group.setLayout(trainers_layout)
layout.addWidget(trainers_group)
# Финансовые показатели
finance_group = QGroupBox("Финансовые показатели и ценовая политика")
finance_layout = QVBoxLayout()
self.finance_table = QTableWidget()
self.finance_table.setColumnCount(4)
self.finance_table.setHorizontalHeaderLabels([
'Период', 'Доход', 'Расходы', 'Прибыль'
])
self.finance_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
finance_layout.addWidget(self.finance_table)
# Управление ценами
price_management_layout = QHBoxLayout()
self.membership_type_combo = QComboBox()
self.new_price_input = QLineEdit()
self.update_price_btn = QPushButton("Обновить цену")
self.membership_type_combo.addItems(['Месячный безлимит', '12 посещений', 'Годовой VIP', 'Разовое посещение', 'Квартальный'])
price_management_layout.addWidget(QLabel("Тип абонемента:"))
price_management_layout.addWidget(self.membership_type_combo)
price_management_layout.addWidget(QLabel("Новая цена:"))
price_management_layout.addWidget(self.new_price_input)
price_management_layout.addWidget(self.update_price_btn)
finance_layout.addLayout(price_management_layout)
finance_group.setLayout(finance_layout)
layout.addWidget(finance_group)
# Стратегические отчеты
reports_group = QGroupBox("Стратегические отчеты")
reports_layout = QHBoxLayout()
reports_left = QVBoxLayout()
self.report_type_combo = QComboBox()
self.report_type_combo.addItems(['Отчет по продажам', 'Отчет по посещаемости', 'Отчет по тренерам', 'Финансовый отчет'])
self.generate_report_btn = QPushButton("Сформировать отчет")
self.report_output = QTextEdit()
reports_left.addWidget(QLabel("Тип отчета:"))
reports_left.addWidget(self.report_type_combo)
reports_left.addWidget(self.generate_report_btn)
reports_left.addWidget(QLabel("Результат:"))
reports_left.addWidget(self.report_output)
reports_right = QVBoxLayout()
self.hire_staff_btn = QPushButton("Принять сотрудника")
self.staff_analytics_btn = QPushButton("Аналитика персонала")
reports_right.addWidget(self.hire_staff_btn)
reports_right.addWidget(self.staff_analytics_btn)
reports_right.addStretch()
reports_layout.addLayout(reports_left)
reports_layout.addLayout(reports_right)
reports_group.setLayout(reports_layout)
layout.addWidget(reports_group)
# Загрузка данных
self.load_director_data()
# Подключение сигналов
self.update_price_btn.clicked.connect(self.update_membership_price)
self.generate_report_btn.clicked.connect(self.generate_strategic_report)
self.hire_staff_btn.clicked.connect(self.hire_staff)
self.staff_analytics_btn.clicked.connect(self.staff_analytics)
self.director_tab.setLayout(layout)
def load_classes_data(self):
"""Загрузка данных о групповых занятиях"""
self.cursor.execute("""
SELECT gc.classID, gc.className, u.fio, gc.classDate, gc.startTime,
gc.endTime, gc.hall, gc.maxParticipants, gc.enrolledParticipants, gc.classStatus
FROM GroupClasses gc
LEFT JOIN Users u ON gc.trainerID = u.userID
ORDER BY gc.classDate, gc.startTime
""")
classes = self.cursor.fetchall()
self.classes_table.setRowCount(len(classes))
for row, class_data in enumerate(classes):
for col, data in enumerate(class_data):
self.classes_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_memberships_data(self):
"""Загрузка данных об абонементах"""
self.cursor.execute("""
SELECT m.membershipID, u.fio, m.membershipType, m.startDate, m.endDate,
m.visitsUsed || '/' || m.visitsTotal, m.membershipStatus, m.cost
FROM Memberships m
JOIN Users u ON m.clientID = u.userID
ORDER BY m.endDate
""")
memberships = self.cursor.fetchall()
self.memberships_table.setRowCount(len(memberships))
for row, membership_data in enumerate(memberships):
for col, data in enumerate(membership_data):
self.memberships_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_hall_occupancy(self):
"""Загрузка данных о загруженности залов"""
# Имитация данных о загруженности
halls = [
('Зал 1', '15/20 (75%)', 'Средняя загруженность'),
('Зал 2', '8/15 (53%)', 'Низкая загруженность'),
('Зал 3', '12/12 (100%)', 'Полная загруженность'),
('Бассейн', '6/10 (60%)', 'Средняя загруженность')
]
self.hall_occupancy_table.setRowCount(len(halls))
for row, hall_data in enumerate(halls):
for col, data in enumerate(hall_data):
self.hall_occupancy_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_attendance_stats(self):
"""Загрузка статистики посещаемости"""
self.cursor.execute("""
SELECT zone, COUNT(*) as visits
FROM Visits
WHERE visitDate >= date('now', '-7 days')
GROUP BY zone
""")
stats = self.cursor.fetchall()
stats_text = ""
for zone, visits in stats:
stats_text += f"{zone}: {visits} посещений\n"
self.attendance_stats.setText(stats_text)
def load_programs_data(self):
"""Загрузка данных о программах тренировок"""
self.cursor.execute("""
SELECT pt.trainingID, u.fio, pt.trainingDate, pt.exercises, pt.notes, pt.progressMetrics
FROM PersonalTraining pt
JOIN Users u ON pt.clientID = u.userID
ORDER BY pt.trainingDate DESC
""")
programs = self.cursor.fetchall()
self.programs_table.setRowCount(len(programs))
for row, program_data in enumerate(programs):
for col, data in enumerate(program_data):
self.programs_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_exercises_data(self):
"""Загрузка данных об упражнениях"""
self.cursor.execute("SELECT * FROM Exercises")
exercises = self.cursor.fetchall()
self.exercises_table.setRowCount(len(exercises))
for row, exercise_data in enumerate(exercises):
for col, data in enumerate(exercise_data):
self.exercises_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_equipment_data(self):
"""Загрузка данных о запросах оборудования"""
self.cursor.execute("""
SELECT requestID, equipment, quantity, requestDate, status
FROM EquipmentRequests
ORDER BY requestDate DESC
""")
equipment = self.cursor.fetchall()
self.equipment_table.setRowCount(len(equipment))
for row, equipment_data in enumerate(equipment):
for col, data in enumerate(equipment_data):
self.equipment_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_trainer_schedule(self):
"""Загрузка расписания тренера"""
# Имитация данных расписания
schedule = [
(1, 'Йога для начинающих', '2024-06-16', '10:00-11:00', 'Запланировано'),
(2, 'Силовая аэробика', '2024-06-16', '18:00-19:00', 'Запланировано'),
(5, 'Бокс', '2024-06-18', '19:00-20:30', 'Запланировано')
]
self.trainer_schedule_table.setRowCount(len(schedule))
for row, schedule_data in enumerate(schedule):
for col, data in enumerate(schedule_data):
self.trainer_schedule_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_class_attendance_stats(self):
"""Загрузка статистики посещаемости занятий тренера"""
stats_text = "Йога для начинающих: 12/15 (80%)\n"
stats_text += "Силовая аэробика: 18/20 (90%)\n"
stats_text += "Бокс: 5/8 (62%)\n"
stats_text += "Средняя посещаемость: 77%"
self.class_attendance_stats.setText(stats_text)
def load_director_data(self):
"""Загрузка данных для директора"""
# Общая статистика
self.cursor.execute("SELECT COUNT(*) FROM Users WHERE userType = 'Клиент'")
total_clients = self.cursor.fetchone()[0]
self.total_members_label.setText(str(total_clients))
self.cursor.execute("SELECT COUNT(*) FROM Memberships WHERE membershipStatus = 'Активен'")
active_memberships = self.cursor.fetchone()[0]
self.active_members_label.setText(str(active_memberships))
self.cursor.execute("""
SELECT SUM(cost) FROM Memberships
WHERE strftime('%Y-%m', startDate) = strftime('%Y-%m', 'now')
""")
monthly_revenue = self.cursor.fetchone()[0] or 0
self.monthly_revenue_label.setText(f"{monthly_revenue:.2f} руб.")
# Прогресс-бары загруженности
self.gym_usage_bar.setValue(75)
self.pool_usage_bar.setValue(60)
self.group_usage_bar.setValue(85)
# Данные по тренерам
self.cursor.execute("""
SELECT u.fio,
COUNT(DISTINCT gc.classID) as group_classes,
COUNT(DISTINCT pt.trainingID) as personal_trainings,
'4.5' as avg_rating,
SUM(m.cost * 0.1) as revenue,
COUNT(DISTINCT pt.trainingID) * 100 as bonuses
FROM Users u
LEFT JOIN GroupClasses gc ON u.userID = gc.trainerID
LEFT JOIN PersonalTraining pt ON u.userID = pt.trainerID
LEFT JOIN Memberships m ON pt.clientID = m.clientID
WHERE u.userType = 'Тренер'
GROUP BY u.userID, u.fio
""")
trainers = self.cursor.fetchall()
self.trainers_table.setRowCount(len(trainers))
for row, trainer_data in enumerate(trainers):
for col, data in enumerate(trainer_data):
self.trainers_table.setItem(row, col, QTableWidgetItem(str(data)))
# Финансовые данные
finance_data = [
('Январь 2024', '150000', '120000', '30000'),
('Февраль 2024', '145000', '118000', '27000'),
('Март 2024', '160000', '125000', '35000'),
('Апрель 2024', '155000', '122000', '33000'),
('Май 2024', '170000', '130000', '40000'),
('Июнь 2024', '165000', '128000', '37000')
]
self.finance_table.setRowCount(len(finance_data))
for row, finance_row in enumerate(finance_data):
for col, data in enumerate(finance_row):
self.finance_table.setItem(row, col, QTableWidgetItem(str(data)))
def add_class(self):
"""Добавление нового группового занятия"""
QMessageBox.information(self, "Информация", "Функция добавления занятия будет реализована в следующей версии")
def assign_trainer(self):
"""Назначение тренера на занятие"""
QMessageBox.information(self, "Информация", "Функция назначения тренера будет реализована в следующей версии")
def change_membership_price(self):
"""Изменение стоимости абонемента"""
QMessageBox.information(self, "Информация", "Функция изменения стоимости будет реализована в следующей версии")
def freeze_membership(self):
"""Заморозка абонемента"""
QMessageBox.information(self, "Информация", "Функция заморозки абонемента будет реализована в следующей версии")
def export_accounting_data(self):
"""Экспорт данных для бухгалтерии"""
QMessageBox.information(self, "Успех", "Данные успешно экспортированы в формате CSV")
def send_mass_notification(self):
"""Отправка массового уведомления"""
message = self.mass_notification_text.toPlainText()
if message:
QMessageBox.information(self, "Успех", f"Массовое уведомление отправлено 150 клиентам:\n\n{message}")
self.mass_notification_text.clear()
else:
QMessageBox.warning(self, "Ошибка", "Введите текст уведомления")
def add_training_program(self):
"""Создание индивидуальной программы тренировок"""
QMessageBox.information(self, "Информация", "Функция создания программы тренировок будет реализована в следующей версии")
def add_exercise(self):
"""Добавление упражнения в базу данных"""
QMessageBox.information(self, "Информация", "Функция добавления упражнения будет реализована в следующей версии")
def request_equipment(self):
"""Запрос дополнительного оборудования"""
QMessageBox.information(self, "Информация", "Функция запроса оборудования будет реализована в следующей версии")
def block_time(self):
"""Блокировка времени для личных нужд"""
QMessageBox.information(self, "Информация", "Функция блокировки времени будет реализована в следующей версии")
def swap_shift(self):
"""Обмен сменами с другими тренерами"""
QMessageBox.information(self, "Информация", "Функция обмена сменами будет реализована в следующей версии")
def view_bonuses(self):
"""Просмотр бонусов за активность клиентов"""
QMessageBox.information(self, "Бонусы", "Ваши бонусы за текущий месяц: 1200 баллов")
def update_membership_price(self):
"""Обновление цены абонемента"""
membership_type = self.membership_type_combo.currentText()
new_price = self.new_price_input.text()
if new_price and new_price.isdigit():
QMessageBox.information(self, "Успех", f"Цена абонемента '{membership_type}' изменена на {new_price} руб.")
self.new_price_input.clear()
else:
QMessageBox.warning(self, "Ошибка", "Введите корректную цену")
def generate_strategic_report(self):
"""Формирование стратегического отчета"""
report_type = self.report_type_combo.currentText()
reports = {
'Отчет по продажам': "Продажи за месяц: 45 абонементов\nОбщий доход: 325,000 руб.\nСамый популярный тип: Месячный безлимит",
'Отчет по посещаемости': "Средняя посещаемость: 78%\nСамая посещаемая зона: Тренажерный зал\nПиковые часы: 18:00-20:00",
'Отчет по тренерам': "Лучший тренер: Петров Д.А.\nСредняя оценка тренеров: 4.7\nКоличество тренировок: 156",
'Финансовый отчет': "Доход: 325,000 руб.\nРасходы: 210,000 руб.\nПрибыль: 115,000 руб.\nРентабельность: 35.4%"
}
self.report_output.setText(f"{report_type}:\n\n{reports[report_type]}")
def hire_staff(self):
"""Принятие нового сотрудника"""
QMessageBox.information(self, "Информация", "Функция приема сотрудников будет реализована в следующей версии")
def staff_analytics(self):
"""Аналитика персонала"""
QMessageBox.information(self, "Аналитика персонала",
"Всего сотрудников: 8\nТренеров: 3\nАдминистраторов: 2\nСредний стаж: 2.5 года\nТекучесть кадров: 12%")
if __name__ == '__main__':
app = QApplication(sys.argv)
# Установка стиля
app.setStyle('Fusion')
window = FitnessApp()
window.show()
sys.exit(app.exec())

View file

@ -5,10 +5,10 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QTabWidget, QTableWidget, QTableWidgetItem,
QPushButton, QLabel, QLineEdit, QComboBox, QDateEdit,
QTextEdit, QMessageBox, QHeaderView, QGroupBox,
QFormLayout, QSpinBox, QCheckBox, QTimeEdit)
from PyQt6.QtCore import Qt, QDate
QFormLayout, QSpinBox, QCheckBox, QTimeEdit, QDialog,
QDialogButtonBox)
from PyQt6.QtCore import Qt, QDate, QTime
from PyQt6.QtGui import QFont, QPalette, QColor
from PyQt6.QtCharts import QChart, QChartView, QPieSeries, QBarSeries, QBarSet, QBarCategoryAxis, QValueAxis
class FitnessApp(QMainWindow):
def __init__(self):
@ -299,9 +299,11 @@ class FitnessApp(QMainWindow):
stats_layout.addLayout(stats_form)
# Правая часть - диаграмма
self.stats_chart = QChartView()
stats_layout.addWidget(self.stats_chart)
# Правая часть - таблица для статистики
self.stats_table = QTableWidget()
self.stats_table.setColumnCount(2)
self.stats_table.setHorizontalHeaderLabels(['Зона', 'Количество посещений'])
stats_layout.addWidget(self.stats_table)
stats_group.setLayout(stats_layout)
layout.addWidget(stats_group)
@ -406,9 +408,11 @@ class FitnessApp(QMainWindow):
overall_layout.addLayout(metrics_layout)
# Правая часть - диаграмма доходов
self.revenue_chart = QChartView()
overall_layout.addWidget(self.revenue_chart)
# Правая часть - таблица доходов по типам абонементов
self.revenue_table = QTableWidget()
self.revenue_table.setColumnCount(2)
self.revenue_table.setHorizontalHeaderLabels(['Тип абонемента', 'Доход'])
overall_layout.addWidget(self.revenue_table)
overall_stats_group.setLayout(overall_layout)
layout.addWidget(overall_stats_group)
@ -526,7 +530,7 @@ class FitnessApp(QMainWindow):
monthly_revenue = self.cursor.fetchone()[0] or 0
self.monthly_revenue_label.setText(f"{monthly_revenue:.2f} руб.")
# Диаграмма доходов
# Таблица доходов по типам абонементов
self.cursor.execute("""
SELECT membershipType, SUM(cost)
FROM Memberships
@ -535,17 +539,10 @@ class FitnessApp(QMainWindow):
""")
revenue_data = self.cursor.fetchall()
series = QPieSeries()
for membership_type, revenue in revenue_data:
series.append(membership_type, revenue)
chart = QChart()
chart.addSeries(series)
chart.setTitle("Доходы по типам абонементов")
chart.legend().setVisible(True)
chart.legend().setAlignment(Qt.AlignmentFlag.AlignBottom)
self.revenue_chart.setChart(chart)
self.revenue_table.setRowCount(len(revenue_data))
for row, (membership_type, revenue) in enumerate(revenue_data):
self.revenue_table.setItem(row, 0, QTableWidgetItem(membership_type))
self.revenue_table.setItem(row, 1, QTableWidgetItem(f"{revenue:.2f} руб."))
# Данные по тренерам
self.cursor.execute("""
@ -598,40 +595,21 @@ class FitnessApp(QMainWindow):
""", (start_date, end_date))
zone_stats = self.cursor.fetchall()
series = QBarSeries()
bar_set = QBarSet("Посещения по зонам")
self.stats_table.setRowCount(len(zone_stats))
for row, (zone, count) in enumerate(zone_stats):
self.stats_table.setItem(row, 0, QTableWidgetItem(zone))
self.stats_table.setItem(row, 1, QTableWidgetItem(str(count)))
categories = []
visits = []
for zone, count in zone_stats:
categories.append(zone)
visits.append(count)
bar_set.append(visits)
series.append(bar_set)
chart = QChart()
chart.addSeries(series)
chart.setTitle(f"Посещаемость по зонам ({start_date} - {end_date})")
axis_x = QBarCategoryAxis()
axis_x.append(categories)
chart.addAxis(axis_x, Qt.AlignmentFlag.AlignBottom)
series.attachAxis(axis_x)
axis_y = QValueAxis()
chart.addAxis(axis_y, Qt.AlignmentFlag.AlignLeft)
series.attachAxis(axis_y)
self.stats_chart.setChart(chart)
class AddClassDialog(QMessageBox):
class AddClassDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Добавить групповое занятие")
self.setModal(True)
layout = QVBoxLayout()
form_layout = QFormLayout()
self.class_name = QLineEdit()
self.trainer = QComboBox()
self.class_date = QDateEdit()
@ -653,21 +631,23 @@ class AddClassDialog(QMessageBox):
self.hall.addItems(['Зал 1', 'Зал 2', 'Зал 3', 'Бассейн'])
layout = QFormLayout()
layout.addRow("Название:", self.class_name)
layout.addRow("Тренер:", self.trainer)
layout.addRow("Дата:", self.class_date)
layout.addRow("Время начала:", self.start_time)
layout.addRow("Время окончания:", self.end_time)
layout.addRow("Зал:", self.hall)
layout.addRow("Макс. участников:", self.max_participants)
form_layout.addRow("Название:", self.class_name)
form_layout.addRow("Тренер:", self.trainer)
form_layout.addRow("Дата:", self.class_date)
form_layout.addRow("Время начала:", self.start_time)
form_layout.addRow("Время окончания:", self.end_time)
form_layout.addRow("Зал:", self.hall)
form_layout.addRow("Макс. участников:", self.max_participants)
widget = QWidget()
widget.setLayout(layout)
self.layout().addWidget(widget, 0, 0, 1, self.layout().columnCount())
layout.addLayout(form_layout)
self.addButton(QPushButton("Добавить"), QMessageBox.ButtonRole.AcceptRole)
self.addButton(QPushButton("Отмена"), QMessageBox.ButtonRole.RejectRole)
# Кнопки
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.setLayout(layout)
def get_data(self):
"""Получение данных из формы"""

693
control2.py.bak Normal file
View file

@ -0,0 +1,693 @@
import sys
import sqlite3
from datetime import datetime, date
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QTabWidget, QTableWidget, QTableWidgetItem,
QPushButton, QLabel, QLineEdit, QComboBox, QDateEdit,
QTextEdit, QMessageBox, QHeaderView, QGroupBox,
QFormLayout, QSpinBox, QCheckBox, QTimeEdit)
from PyQt6.QtCore import Qt, QDate
from PyQt6.QtGui import QFont, QPalette, QColor
from PyQt6.QtCharts import QChart, QChartView, QPieSeries, QBarSeries, QBarSet, QBarCategoryAxis, QValueAxis
class FitnessApp(QMainWindow):
def __init__(self):
super().__init__()
self.initDB()
self.initUI()
def initDB(self):
"""Инициализация базы данных"""
self.conn = sqlite3.connect('fitness.db')
self.cursor = self.conn.cursor()
# Создание таблиц
self.create_tables()
# Заполнение тестовыми данными
self.insert_sample_data()
def create_tables(self):
"""Создание таблиц базы данных"""
tables = [
"""
CREATE TABLE IF NOT EXISTS Users (
userID INTEGER PRIMARY KEY,
fio TEXT NOT NULL,
phone TEXT,
email TEXT,
login TEXT UNIQUE,
password TEXT,
userType TEXT,
specialization TEXT,
birthDate DATE
)
""",
"""
CREATE TABLE IF NOT EXISTS Memberships (
membershipID INTEGER PRIMARY KEY,
clientID INTEGER,
membershipType TEXT,
startDate DATE,
endDate DATE,
visitsTotal INTEGER,
visitsUsed INTEGER,
zones TEXT,
membershipStatus TEXT,
cost REAL,
adminID INTEGER,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (adminID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS Visits (
visitID INTEGER PRIMARY KEY,
clientID INTEGER,
visitDate DATE,
checkInTime TIME,
checkOutTime TIME,
zone TEXT,
membershipID INTEGER,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (membershipID) REFERENCES Memberships(membershipID)
)
""",
"""
CREATE TABLE IF NOT EXISTS GroupClasses (
classID INTEGER PRIMARY KEY,
className TEXT,
trainerID INTEGER,
classDate DATE,
startTime TIME,
endTime TIME,
hall TEXT,
maxParticipants INTEGER,
enrolledParticipants INTEGER,
classStatus TEXT,
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS PersonalTraining (
trainingID INTEGER PRIMARY KEY,
clientID INTEGER,
trainerID INTEGER,
trainingDate DATE,
startTime TIME,
endTime TIME,
exercises TEXT,
notes TEXT,
progressMetrics TEXT,
FOREIGN KEY (clientID) REFERENCES Users(userID),
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS ClassRegistrations (
registrationID INTEGER PRIMARY KEY,
classID INTEGER,
clientID INTEGER,
registrationDate DATE,
status TEXT,
FOREIGN KEY (classID) REFERENCES GroupClasses(classID),
FOREIGN KEY (clientID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS EquipmentRequests (
requestID INTEGER PRIMARY KEY,
trainerID INTEGER,
equipment TEXT,
quantity INTEGER,
requestDate DATE,
status TEXT,
FOREIGN KEY (trainerID) REFERENCES Users(userID)
)
""",
"""
CREATE TABLE IF NOT EXISTS ShiftSwaps (
swapID INTEGER PRIMARY KEY,
trainerID1 INTEGER,
trainerID2 INTEGER,
shiftDate DATE,
status TEXT,
FOREIGN KEY (trainerID1) REFERENCES Users(userID),
FOREIGN KEY (trainerID2) REFERENCES Users(userID)
)
"""
]
for table in tables:
self.cursor.execute(table)
self.conn.commit()
def insert_sample_data(self):
"""Вставка тестовых данных"""
# Проверяем, есть ли уже данные
self.cursor.execute("SELECT COUNT(*) FROM Users")
if self.cursor.fetchone()[0] > 0:
return
users = [
(1, 'Сидорова Марина Петровна', '89219014567', 'director@fitness.ru', 'director1', 'pass1', 'Директор', '', '1980-05-15'),
(2, 'Романова Анна Сергеевна', '89210125678', 'admin1@fitness.ru', 'admin1', 'pass2', 'Администратор', '', '1992-08-22'),
(4, 'Яковлева Елена Викторовна', '89211236789', 'admin2@fitness.ru', 'admin2', 'pass3', 'Администратор', '', '1988-11-10'),
(7, 'Петров Дмитрий Александрович', '89212347890', 'petrov@fitness.ru', 'trainer1', 'pass4', 'Тренер', 'Силовые тренировки', '1985-03-18'),
(9, 'Смирнова Ольга Игоревна', '89213458901', 'smirnova@fitness.ru', 'trainer2', 'pass5', 'Тренер', 'Йога, Пилатес', '1990-07-25'),
(11, 'Козлов Сергей Николаевич', '89214569012', 'kozlov@fitness.ru', 'trainer3', 'pass6', 'Тренер', 'Плавание', '1987-12-05'),
(16, 'Федорова Екатерина Дмитриевна', '89161112236', 'fedorova@mail.ru', 'client1', 'pass7', 'Клиент', '', '1995-04-12'),
(21, 'Михайлов Алексей Владимирович', '89162223347', 'mikhailov@gmail.com', 'client2', 'pass8', 'Клиент', '', '1988-09-30'),
(26, 'Новикова Ирина Сергеевна', '89163334458', 'novikova@yandex.ru', 'client3', 'pass9', 'Клиент', '', '1992-06-18'),
(30, 'Соколов Игорь Петрович', '89164445569', 'sokolov@mail.ru', 'client4', 'pass10', 'Клиент', '', '1983-02-28'),
(34, 'Павлова Мария Александровна', '89165556670', 'pavlova@gmail.com', 'client5', 'pass11', 'Клиент', '', '1997-11-07')
]
memberships = [
(1, 16, 'Месячный безлимит', '2024-06-01', '2024-06-30', 999, 42, 'Зал, Бассейн, Групповые', 'Активен', 5000.00, 2),
(2, 21, '12 посещений', '2024-06-05', '2024-09-05', 12, 8, 'Зал', 'Активен', 4000.00, 2),
(3, 26, 'Годовой VIP', '2024-01-10', '2025-01-10', 999, 156, 'Все зоны', 'Активен', 45000.00, 4),
(4, 30, 'Разовое посещение', '2024-06-15', '2024-06-15', 1, 1, 'Зал', 'Завершен', 500.00, 2),
(5, 34, 'Квартальный', '2024-06-01', '2024-08-31', 999, 15, 'Зал, Групповые', 'Активен', 12000.00, 4)
]
visits = [
(1, 16, '2024-06-15', '08:30', '10:15', 'Тренажерный зал', 1),
(2, 21, '2024-06-15', '09:00', '10:30', 'Тренажерный зал', 2),
(3, 26, '2024-06-15', '07:00', '08:30', 'Бассейн', 3),
(4, 16, '2024-06-15', '18:00', '19:45', 'Групповое занятие', 1),
(5, 34, '2024-06-15', '19:00', '20:30', 'Групповое занятие', 5)
]
group_classes = [
(1, 'Йога для начинающих', 9, '2024-06-16', '10:00', '11:00', 'Зал 2', 15, 12, 'Запланировано'),
(2, 'Силовая аэробика', 7, '2024-06-16', '18:00', '19:00', 'Зал 1', 20, 18, 'Запланировано'),
(3, 'Пилатес', 9, '2024-06-17', '11:00', '12:00', 'Зал 2', 12, 12, 'Группа заполнена'),
(4, 'Аквааэробика', 11, '2024-06-17', '15:00', '16:00', 'Бассейн', 10, 7, 'Запланировано'),
(5, 'Бокс', 7, '2024-06-18', '19:00', '20:30', 'Зал 3', 8, 5, 'Запланировано')
]
personal_training = [
(1, 26, 7, '2024-06-14', '16:00', '17:00', 'Жим лежа, Приседания, Тяга блока', 'Хорошая техника', 'Жим 80кг x 8'),
(2, 16, 9, '2024-06-13', '10:00', '11:00', 'Асаны йоги, Растяжка', 'Улучшилась гибкость', ''),
(3, 21, 7, '2024-06-12', '14:00', '15:00', 'Становая тяга, Жим гантелей', 'Нужно работать над техникой', 'Становая 60кг x 6')
]
# Вставка данных
self.cursor.executemany("INSERT INTO Users VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", users)
self.cursor.executemany("INSERT INTO Memberships VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", memberships)
self.cursor.executemany("INSERT INTO Visits VALUES (?, ?, ?, ?, ?, ?, ?)", visits)
self.cursor.executemany("INSERT INTO GroupClasses VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", group_classes)
self.cursor.executemany("INSERT INTO PersonalTraining VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", personal_training)
self.conn.commit()
def initUI(self):
"""Инициализация пользовательского интерфейса"""
self.setWindowTitle('Фитнес-клуб - Система управления')
self.setGeometry(100, 100, 1200, 800)
# Центральный виджет с вкладками для разных ролей
self.tabs = QTabWidget()
# Вкладка для администратора
self.admin_tab = QWidget()
self.init_admin_tab()
self.tabs.addTab(self.admin_tab, "Администратор")
# Вкладка для тренера
self.trainer_tab = QWidget()
self.init_trainer_tab()
self.tabs.addTab(self.trainer_tab, "Тренер")
# Вкладка для директора
self.director_tab = QWidget()
self.init_director_tab()
self.tabs.addTab(self.director_tab, "Директор")
self.setCentralWidget(self.tabs)
def init_admin_tab(self):
"""Инициализация вкладки администратора"""
layout = QVBoxLayout()
# Группа управления расписанием
schedule_group = QGroupBox("Управление расписанием групповых занятий")
schedule_layout = QVBoxLayout()
# Таблица групповых занятий
self.classes_table = QTableWidget()
self.classes_table.setColumnCount(10)
self.classes_table.setHorizontalHeaderLabels([
'ID', 'Название', 'Тренер', 'Дата', 'Время начала',
'Время окончания', 'Зал', 'Макс. участников', 'Записано', 'Статус'
])
schedule_layout.addWidget(self.classes_table)
# Кнопки управления
btn_layout = QHBoxLayout()
self.add_class_btn = QPushButton("Добавить занятие")
self.edit_class_btn = QPushButton("Редактировать")
self.delete_class_btn = QPushButton("Удалить")
btn_layout.addWidget(self.add_class_btn)
btn_layout.addWidget(self.edit_class_btn)
btn_layout.addWidget(self.delete_class_btn)
schedule_layout.addLayout(btn_layout)
schedule_group.setLayout(schedule_layout)
layout.addWidget(schedule_group)
# Группа управления абонементами
membership_group = QGroupBox("Управление абонементами")
membership_layout = QVBoxLayout()
self.memberships_table = QTableWidget()
self.memberships_table.setColumnCount(8)
self.memberships_table.setHorizontalHeaderLabels([
'ID', 'Клиент', 'Тип', 'Начало', 'Окончание',
'Использовано/Всего', 'Статус', 'Стоимость'
])
membership_layout.addWidget(self.memberships_table)
# Кнопки для управления абонементами
membership_btn_layout = QHBoxLayout()
self.change_price_btn = QPushButton("Изменить стоимость")
self.freeze_membership_btn = QPushButton("Заморозить абонемент")
membership_btn_layout.addWidget(self.change_price_btn)
membership_btn_layout.addWidget(self.freeze_membership_btn)
membership_layout.addLayout(membership_btn_layout)
membership_group.setLayout(membership_layout)
layout.addWidget(membership_group)
# Группа статистики
stats_group = QGroupBox("Статистика и отчетность")
stats_layout = QHBoxLayout()
# Левая часть - форма для фильтров
stats_form = QFormLayout()
self.stats_start_date = QDateEdit()
self.stats_end_date = QDateEdit()
self.stats_start_date.setDate(QDate.currentDate().addMonths(-1))
self.stats_end_date.setDate(QDate.currentDate())
stats_form.addRow("С:", self.stats_start_date)
stats_form.addRow("По:", self.stats_end_date)
self.generate_stats_btn = QPushButton("Сформировать отчет")
stats_form.addRow(self.generate_stats_btn)
stats_layout.addLayout(stats_form)
# Правая часть - диаграмма
self.stats_chart = QChartView()
stats_layout.addWidget(self.stats_chart)
stats_group.setLayout(stats_layout)
layout.addWidget(stats_group)
# Загрузка данных
self.load_classes_data()
self.load_memberships_data()
# Подключение сигналов
self.add_class_btn.clicked.connect(self.add_class)
self.generate_stats_btn.clicked.connect(self.generate_stats)
self.admin_tab.setLayout(layout)
def init_trainer_tab(self):
"""Инициализация вкладки тренера"""
layout = QVBoxLayout()
# Группа программ тренировок
programs_group = QGroupBox("Индивидуальные программы тренировок")
programs_layout = QVBoxLayout()
self.programs_table = QTableWidget()
self.programs_table.setColumnCount(6)
self.programs_table.setHorizontalHeaderLabels([
'ID', 'Клиент', 'Дата', 'Упражнения', 'Заметки', 'Прогресс'
])
programs_layout.addWidget(self.programs_table)
programs_btn_layout = QHBoxLayout()
self.add_program_btn = QPushButton("Добавить программу")
self.edit_program_btn = QPushButton("Редактировать")
programs_btn_layout.addWidget(self.add_program_btn)
programs_btn_layout.addWidget(self.edit_program_btn)
programs_layout.addLayout(programs_btn_layout)
programs_group.setLayout(programs_layout)
layout.addWidget(programs_group)
# Группа базы упражнений
exercises_group = QGroupBox("База упражнений")
exercises_layout = QVBoxLayout()
self.exercises_table = QTableWidget()
self.exercises_table.setColumnCount(3)
self.exercises_table.setHorizontalHeaderLabels(['ID', 'Название', 'Группа мышц'])
exercises_layout.addWidget(self.exercises_table)
exercises_btn_layout = QHBoxLayout()
self.add_exercise_btn = QPushButton("Добавить упражнение")
exercises_btn_layout.addWidget(self.add_exercise_btn)
exercises_layout.addLayout(exercises_btn_layout)
exercises_group.setLayout(exercises_layout)
layout.addWidget(exercises_group)
# Группа запросов оборудования
equipment_group = QGroupBox("Запросы оборудования")
equipment_layout = QVBoxLayout()
self.equipment_table = QTableWidget()
self.equipment_table.setColumnCount(5)
self.equipment_table.setHorizontalHeaderLabels([
'ID', 'Оборудование', 'Количество', 'Дата запроса', 'Статус'
])
equipment_layout.addWidget(self.equipment_table)
equipment_btn_layout = QHBoxLayout()
self.request_equipment_btn = QPushButton("Запросить оборудование")
equipment_btn_layout.addWidget(self.request_equipment_btn)
equipment_layout.addLayout(equipment_btn_layout)
equipment_group.setLayout(equipment_layout)
layout.addWidget(equipment_group)
# Загрузка данных
self.load_programs_data()
self.load_equipment_data()
self.trainer_tab.setLayout(layout)
def init_director_tab(self):
"""Инициализация вкладки директора"""
layout = QVBoxLayout()
# Общая статистика
overall_stats_group = QGroupBox("Общая статистика клуба")
overall_layout = QHBoxLayout()
# Левая часть - ключевые показатели
metrics_layout = QFormLayout()
self.total_members_label = QLabel("0")
self.active_members_label = QLabel("0")
self.monthly_revenue_label = QLabel("0 руб.")
self.attendance_rate_label = QLabel("0%")
metrics_layout.addRow("Всего клиентов:", self.total_members_label)
metrics_layout.addRow("Активных абонементов:", self.active_members_label)
metrics_layout.addRow("Доход за месяц:", self.monthly_revenue_label)
metrics_layout.addRow("Посещаемость:", self.attendance_rate_label)
overall_layout.addLayout(metrics_layout)
# Правая часть - диаграмма доходов
self.revenue_chart = QChartView()
overall_layout.addWidget(self.revenue_chart)
overall_stats_group.setLayout(overall_layout)
layout.addWidget(overall_stats_group)
# Эффективность тренеров
trainers_group = QGroupBox("Эффективность тренеров")
trainers_layout = QVBoxLayout()
self.trainers_table = QTableWidget()
self.trainers_table.setColumnCount(6)
self.trainers_table.setHorizontalHeaderLabels([
'Тренер', 'Групповые занятия', 'Персональные тренировки',
'Средняя оценка', 'Доход', 'Бонусы'
])
trainers_layout.addWidget(self.trainers_table)
trainers_group.setLayout(trainers_layout)
layout.addWidget(trainers_group)
# Финансовые показатели
finance_group = QGroupBox("Финансовые показатели")
finance_layout = QVBoxLayout()
self.finance_table = QTableWidget()
self.finance_table.setColumnCount(4)
self.finance_table.setHorizontalHeaderLabels([
'Период', 'Доход', 'Расходы', 'Прибыль'
])
finance_layout.addWidget(self.finance_table)
finance_group.setLayout(finance_layout)
layout.addWidget(finance_group)
# Загрузка данных
self.load_director_data()
self.director_tab.setLayout(layout)
def load_classes_data(self):
"""Загрузка данных о групповых занятиях"""
self.cursor.execute("""
SELECT gc.classID, gc.className, u.fio, gc.classDate, gc.startTime,
gc.endTime, gc.hall, gc.maxParticipants, gc.enrolledParticipants, gc.classStatus
FROM GroupClasses gc
LEFT JOIN Users u ON gc.trainerID = u.userID
ORDER BY gc.classDate, gc.startTime
""")
classes = self.cursor.fetchall()
self.classes_table.setRowCount(len(classes))
for row, class_data in enumerate(classes):
for col, data in enumerate(class_data):
self.classes_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_memberships_data(self):
"""Загрузка данных об абонементах"""
self.cursor.execute("""
SELECT m.membershipID, u.fio, m.membershipType, m.startDate, m.endDate,
m.visitsUsed || '/' || m.visitsTotal, m.membershipStatus, m.cost
FROM Memberships m
JOIN Users u ON m.clientID = u.userID
ORDER BY m.endDate
""")
memberships = self.cursor.fetchall()
self.memberships_table.setRowCount(len(memberships))
for row, membership_data in enumerate(memberships):
for col, data in enumerate(membership_data):
self.memberships_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_programs_data(self):
"""Загрузка данных о программах тренировок"""
self.cursor.execute("""
SELECT pt.trainingID, u.fio, pt.trainingDate, pt.exercises, pt.notes, pt.progressMetrics
FROM PersonalTraining pt
JOIN Users u ON pt.clientID = u.userID
ORDER BY pt.trainingDate DESC
""")
programs = self.cursor.fetchall()
self.programs_table.setRowCount(len(programs))
for row, program_data in enumerate(programs):
for col, data in enumerate(program_data):
self.programs_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_equipment_data(self):
"""Загрузка данных о запросах оборудования"""
self.cursor.execute("""
SELECT requestID, equipment, quantity, requestDate, status
FROM EquipmentRequests
ORDER BY requestDate DESC
""")
equipment = self.cursor.fetchall()
self.equipment_table.setRowCount(len(equipment))
for row, equipment_data in enumerate(equipment):
for col, data in enumerate(equipment_data):
self.equipment_table.setItem(row, col, QTableWidgetItem(str(data)))
def load_director_data(self):
"""Загрузка данных для директора"""
# Общая статистика
self.cursor.execute("SELECT COUNT(*) FROM Users WHERE userType = 'Клиент'")
total_clients = self.cursor.fetchone()[0]
self.total_members_label.setText(str(total_clients))
self.cursor.execute("SELECT COUNT(*) FROM Memberships WHERE membershipStatus = 'Активен'")
active_memberships = self.cursor.fetchone()[0]
self.active_members_label.setText(str(active_memberships))
self.cursor.execute("""
SELECT SUM(cost) FROM Memberships
WHERE strftime('%Y-%m', startDate) = strftime('%Y-%m', 'now')
""")
monthly_revenue = self.cursor.fetchone()[0] or 0
self.monthly_revenue_label.setText(f"{monthly_revenue:.2f} руб.")
# Диаграмма доходов
self.cursor.execute("""
SELECT membershipType, SUM(cost)
FROM Memberships
WHERE strftime('%Y', startDate) = strftime('%Y', 'now')
GROUP BY membershipType
""")
revenue_data = self.cursor.fetchall()
series = QPieSeries()
for membership_type, revenue in revenue_data:
series.append(membership_type, revenue)
chart = QChart()
chart.addSeries(series)
chart.setTitle("Доходы по типам абонементов")
chart.legend().setVisible(True)
chart.legend().setAlignment(Qt.AlignmentFlag.AlignBottom)
self.revenue_chart.setChart(chart)
# Данные по тренерам
self.cursor.execute("""
SELECT u.fio,
COUNT(DISTINCT gc.classID) as group_classes,
COUNT(DISTINCT pt.trainingID) as personal_trainings,
'4.5' as avg_rating,
SUM(m.cost * 0.1) as revenue,
COUNT(DISTINCT pt.trainingID) * 100 as bonuses
FROM Users u
LEFT JOIN GroupClasses gc ON u.userID = gc.trainerID
LEFT JOIN PersonalTraining pt ON u.userID = pt.trainerID
LEFT JOIN Memberships m ON pt.clientID = m.clientID
WHERE u.userType = 'Тренер'
GROUP BY u.userID, u.fio
""")
trainers = self.cursor.fetchall()
self.trainers_table.setRowCount(len(trainers))
for row, trainer_data in enumerate(trainers):
for col, data in enumerate(trainer_data):
self.trainers_table.setItem(row, col, QTableWidgetItem(str(data)))
def add_class(self):
"""Добавление нового группового занятия"""
dialog = AddClassDialog(self)
if dialog.exec():
class_data = dialog.get_data()
try:
self.cursor.execute("""
INSERT INTO GroupClasses (className, trainerID, classDate, startTime, endTime, hall, maxParticipants, enrolledParticipants, classStatus)
VALUES (?, ?, ?, ?, ?, ?, ?, 0, 'Запланировано')
""", class_data)
self.conn.commit()
self.load_classes_data()
QMessageBox.information(self, "Успех", "Занятие успешно добавлено!")
except Exception as e:
QMessageBox.critical(self, "Ошибка", f"Не удалось добавить занятие: {str(e)}")
def generate_stats(self):
"""Генерация статистики посещаемости"""
start_date = self.stats_start_date.date().toString("yyyy-MM-dd")
end_date = self.stats_end_date.date().toString("yyyy-MM-dd")
self.cursor.execute("""
SELECT zone, COUNT(*) as visits
FROM Visits
WHERE visitDate BETWEEN ? AND ?
GROUP BY zone
""", (start_date, end_date))
zone_stats = self.cursor.fetchall()
series = QBarSeries()
bar_set = QBarSet("Посещения по зонам")
categories = []
visits = []
for zone, count in zone_stats:
categories.append(zone)
visits.append(count)
bar_set.append(visits)
series.append(bar_set)
chart = QChart()
chart.addSeries(series)
chart.setTitle(f"Посещаемость по зонам ({start_date} - {end_date})")
axis_x = QBarCategoryAxis()
axis_x.append(categories)
chart.addAxis(axis_x, Qt.AlignmentFlag.AlignBottom)
series.attachAxis(axis_x)
axis_y = QValueAxis()
chart.addAxis(axis_y, Qt.AlignmentFlag.AlignLeft)
series.attachAxis(axis_y)
self.stats_chart.setChart(chart)
class AddClassDialog(QMessageBox):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Добавить групповое занятие")
self.setModal(True)
self.class_name = QLineEdit()
self.trainer = QComboBox()
self.class_date = QDateEdit()
self.start_time = QTimeEdit()
self.end_time = QTimeEdit()
self.hall = QComboBox()
self.max_participants = QSpinBox()
self.class_date.setDate(QDate.currentDate())
self.start_time.setTime(QTime.currentTime())
self.end_time.setTime(QTime.currentTime().addSecs(3600))
self.max_participants.setRange(1, 100)
# Заполнение комбобоксов
parent.cursor.execute("SELECT userID, fio FROM Users WHERE userType = 'Тренер'")
trainers = parent.cursor.fetchall()
for trainer_id, fio in trainers:
self.trainer.addItem(fio, trainer_id)
self.hall.addItems(['Зал 1', 'Зал 2', 'Зал 3', 'Бассейн'])
layout = QFormLayout()
layout.addRow("Название:", self.class_name)
layout.addRow("Тренер:", self.trainer)
layout.addRow("Дата:", self.class_date)
layout.addRow("Время начала:", self.start_time)
layout.addRow("Время окончания:", self.end_time)
layout.addRow("Зал:", self.hall)
layout.addRow("Макс. участников:", self.max_participants)
widget = QWidget()
widget.setLayout(layout)
self.layout().addWidget(widget, 0, 0, 1, self.layout().columnCount())
self.addButton(QPushButton("Добавить"), QMessageBox.ButtonRole.AcceptRole)
self.addButton(QPushButton("Отмена"), QMessageBox.ButtonRole.RejectRole)
def get_data(self):
"""Получение данных из формы"""
return (
self.class_name.text(),
self.trainer.currentData(),
self.class_date.date().toString("yyyy-MM-dd"),
self.start_time.time().toString("hh:mm"),
self.end_time.time().toString("hh:mm"),
self.hall.currentText(),
self.max_participants.value()
)
if __name__ == '__main__':
app = QApplication(sys.argv)
# Установка стиля
app.setStyle('Fusion')
window = FitnessApp()
window.show()
sys.exit(app.exec())

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
service_requests_v2.db Normal file

Binary file not shown.