Загрузка данных


import customtkinter as ctk
import mysql.connector
from tkinter import messagebox, ttk
import re

ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")

class AutoServiceApp(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("Автосервис")
        self.geometry("450x550")
        self.db_config = {'host': 'LocalHost', 'user': 'root', 'password': 'Ruslan2020gog', 'database': 'autoservice_db'}
        self.current_user = None
        self.services_data = {}
        self.mechanics_data = {}
        self.show_login_screen()

    def connect(self):
        return mysql.connector.connect(**self.db_config)

    def show_login_screen(self):
        for widget in self.winfo_children(): widget.destroy()
        self.geometry("450x550")
        ctk.CTkLabel(self, text="Авторизация", font=("Arial", 26, "bold")).pack(pady=(50, 30))
        self.login_entry = ctk.CTkEntry(self, placeholder_text="Логин", width=300, height=40)
        self.login_entry.pack(pady=10)
        self.pass_entry = ctk.CTkEntry(self, placeholder_text="Пароль", show="*", width=300, height=40)
        self.pass_entry.pack(pady=10)
        ctk.CTkButton(self, text="Войти", width=300, height=45, font=("Arial", 16, "bold"), command=self.login).pack(pady=20)
        ctk.CTkButton(self, text="Регистрация", width=300, height=45, fg_color="transparent", border_width=2, border_color="#3b8ed0", command=self.show_register_screen).pack()

    def show_register_screen(self):
        for widget in self.winfo_children(): widget.destroy()
        ctk.CTkLabel(self, text="Регистрация", font=("Arial", 26, "bold")).pack(pady=(40, 20))
        self.reg_login = ctk.CTkEntry(self, placeholder_text="Логин", width=300, height=40)
        self.reg_login.pack(pady=10)
        self.reg_mail = ctk.CTkEntry(self, placeholder_text="Почта", width=300, height=40)
        self.reg_mail.pack(pady=10)
        self.reg_pass = ctk.CTkEntry(self, placeholder_text="Пароль", show="*", width=300, height=40)
        self.reg_pass.pack(pady=10)
        ctk.CTkButton(self, text="Создать аккаунт", width=300, height=45, command=self.register).pack(pady=20)
        ctk.CTkButton(self, text="Назад", fg_color="transparent", command=self.show_login_screen).pack()

    def validate_password(self, p):
        if len(p) < 4: return False, "Пароль слишком короткий!"
        if not re.search(r"[A-Z]", p): return False, "Нужна заглавная буква!"
        if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", p): return False, "Нужен спецсимвол!"
        if re.search(r"[а-яА-Я]", p): return False, "Только латиница!"
        return True, ""
    
    def validate_mail(self, m):
        if len(m) < 14: return False, "Длина почты слишком короткая!"
        if not re.search(r"[@]", m): return False, "не найдено символа @"
        if not re.search(r"[.com]", m): return False, "почта введена неверно"
        if not re.search(r"[.net]", m): return False, "почта введена неверно"
        if not re.search(r"[.ru]", m): return False, "почта введена неверно"
        if re.search(r"[а-яА-Я]", m): return False, "Только латиница!"
        return True, ""

    def register(self):
        u, p, m = self.reg_login.get().strip(), self.reg_pass.get().strip()
        if not u or not p or not m: return
        v, msg = self.validate_password(p)
        m, msg = self.validate_mail(m)
        if not v:
            messagebox.showerror("Ошибка", msg)
            return
        if not m:
            messagebox.showerror("Ошибка", msg)
            return
        try:
            conn = self.connect(); cursor = conn.cursor()
            cursor.execute("INSERT INTO users (username, password, role, mail) VALUES (%s, %s, 'client', %s)", (u, p))
            conn.commit(); conn.close(); self.show_login_screen()
        except: messagebox.showerror("Ошибка", "Логин занят")

    def login(self):
        u, p = self.login_entry.get().strip(), self.pass_entry.get().strip()
        try:
            conn = self.connect(); cursor = conn.cursor()
            cursor.execute("SELECT username, role FROM users WHERE username=%s AND password=%s", (u, p))
            user = cursor.fetchone(); conn.close()
            if user:
                self.current_user = user
                self.load_dictionaries(); self.show_main_interface()
            else: messagebox.showerror("Ошибка", "Неверные данные")
        except Exception as e: messagebox.showerror("БД", str(e))

    def load_dictionaries(self):
        conn = self.connect(); cursor = conn.cursor()
        cursor.execute("SELECT id, service_name FROM services")
        self.services_data = {name: id for id, name in cursor.fetchall()}
        cursor.execute("SELECT id, fio FROM mechanics")
        self.mechanics_data = {fio: id for id, fio in cursor.fetchall()}
        conn.close()

    def show_main_interface(self):
        for widget in self.winfo_children(): widget.destroy()
        self.geometry("1300x750")
        tp = ctk.CTkFrame(self, height=60, fg_color="#212121")
        tp.pack(fill="x", padx=10, pady=5)
        ctk.CTkLabel(tp, text=f"Пользователь: {self.current_user[0]}", font=("Arial", 15, "bold")).pack(side="left", padx=20)
        ctk.CTkButton(tp, text="Выйти", width=100, fg_color="#e74c3c", command=self.show_login_screen).pack(side="right", padx=20)
        cont = ctk.CTkFrame(self)
        cont.pack(expand=True, fill="both", padx=10, pady=10)
        cont.grid_columnconfigure(1, weight=1); cont.grid_rowconfigure(0, weight=1)
        sb = ctk.CTkFrame(cont, width=350); sb.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
        ctk.CTkLabel(sb, text="Оформление записи", font=("Arial", 20, "bold")).pack(pady=15)
        self.client_name = ctk.CTkEntry(sb, placeholder_text="Ваше ФИО", width=280); self.client_name.pack(pady=5)
        self.car_model = ctk.CTkEntry(sb, placeholder_text="Автомобиль", width=280); self.car_model.pack(pady=5)
        ctk.CTkLabel(sb, text="Услуга:").pack()
        self.service_cb = ctk.CTkComboBox(sb, values=list(self.services_data.keys()), width=280); self.service_cb.pack(pady=5)
        ctk.CTkLabel(sb, text="Мастер:").pack()
        self.mechanic_cb = ctk.CTkComboBox(sb, values=list(self.mechanics_data.keys()), width=280); self.mechanic_cb.pack(pady=5)
        self.app_date = ctk.CTkEntry(sb, placeholder_text="Дата (ГГГГ-ММ-ДД)", width=280); self.app_date.pack(pady=10)
        ctk.CTkButton(sb, text="Создать запись", fg_color="#2ecc71", command=self.confirm_creation).pack(pady=10)
        ctk.CTkButton(sb, text="Удалить запись", fg_color="#e74c3c", command=self.confirm_deletion).pack(pady=5)
        tf = ctk.CTkFrame(cont); tf.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)
        self.tree = ttk.Treeview(tf, columns=("ID", "Клиент", "Авто", "Услуга", "Цена", "Мастер", "Дата", "Номер", "VIN-Номер"), show='headings')
        for col, w in [("ID", 40), ("Клиент", 180), ("Авто", 130), ("Услуга", 160), ("Цена", 70), ("Мастер", 180), ("Дата", 100), ("Номер", 80), ("VIN-Номер", 80)]:
            self.tree.heading(col, text=col); self.tree.column(col, width=w, anchor="center")
        self.tree.pack(expand=True, fill="both", padx=10, pady=10); self.refresh_data()

    def confirm_creation(self):
        if messagebox.askyesno("Запись", "Подтвердить?"): self.save_record()

    def save_record(self):
        try:
            s_id, m_id = self.services_data[self.service_cb.get()], self.mechanics_data[self.mechanic_cb.get()]
            conn = self.connect(); cursor = conn.cursor()
            cursor.execute("INSERT INTO clients (full_name, phone) VALUES (%s, %s)", (self.client_name.get(), self.current_user[0]))
            c_id = cursor.lastrowid
            cursor.execute("INSERT INTO cars (client_id, brand_model) VALUES (%s, %s)", (c_id, self.car_model.get()))
            car_id = cursor.lastrowid
            cursor.execute("INSERT INTO appointments (car_id, service_id, mechanic_id, appointment_date) VALUES (%s, %s, %s, %s)", (car_id, s_id, m_id, self.app_date.get()))
            conn.commit(); conn.close(); self.refresh_data()
        except Exception as e: messagebox.showerror("Ошибка", str(e))

    def confirm_deletion(self):
        sel = self.tree.selection()
        if not sel: return
        v = self.tree.item(sel[0])['values']
        rec_id = v[0]
        conn = self.connect(); cursor = conn.cursor()
        cursor.execute("SELECT c.phone FROM clients c JOIN cars cr ON c.id = cr.client_id JOIN appointments a ON cr.id = a.car_id WHERE a.id = %s", (rec_id,))
        res = cursor.fetchone(); conn.close()
        if self.current_user[1] == 'admin' or (res and str(res[0]) == str(self.current_user[0])):
            if messagebox.askyesno("Удаление", "Удалить?"):
                conn = self.connect(); cursor = conn.cursor()
                cursor.execute("DELETE FROM appointments WHERE id = %s", (rec_id,)); conn.commit(); conn.close(); self.refresh_data()
        else: messagebox.showerror("Отказ", "Это не ваша запись")

    def refresh_data(self):
        for i in self.tree.get_children(): self.tree.delete(i)
        conn = self.connect(); cursor = conn.cursor()
        cursor.execute("SELECT a.id, c.full_name, cr.brand_model, s.service_name, s.price, m.fio, a.appointment_date FROM appointments a LEFT JOIN cars cr ON a.car_id = cr.id LEFT JOIN clients c ON cr.client_id = c.id LEFT JOIN services s ON a.service_id = s.id LEFT JOIN mechanics m ON a.mechanic_id = m.id ORDER BY a.id DESC")
        for r in cursor.fetchall(): self.tree.insert("", "end", values=r)
        conn.close()

if __name__ == "__main__":
    app = AutoServiceApp(); app.mainloop()