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


import tkinter as tk
from tkinter import ttk, messagebox
import mysql.connector
from mysql.connector import Error

# Конфигурация подключения к MySQL (измени пароль и пользователя, если отличаются)
DB_CONFIG = {
    'host': 'localhost',
    'user': 'root',
    'password': 'root',  -- укажи свой пароль от MySQL Workbench
    'database': 'health'
}

class MedicalCenterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Информационная система: Медицинский центр Здоровье")
        self.root.geometry("1000x650")
        
        # Настройка стиля интерфейса
        style = ttk.Style()
        style.theme_use('clam')
        
        # Создание главного контейнера вкладок
        self.notebook = ttk.Notebook(root)
        self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
        
        # Инициализация вкладок
        self.init_reports_tab()
        self.init_view_tables_tab()
        self.init_add_data_tab()
        
    def execute_query(self, query, params=None):
        """Универсальный метод для безопасного выполнения запросов чтения из БД"""
        connection = None
        try:
            connection = mysql.connector.connect(**DB_CONFIG)
            cursor = connection.cursor()
            if params:
                cursor.execute(query, params)
            else:
                cursor.execute(query)
            
            headers = [desc[0] for desc in cursor.description]
            rows = cursor.fetchall()
            return headers, rows
        except Error as e:
            messagebox.showerror("Ошибка базы данных", f"Не удалось выполнить запрос:\n{e}")
            return None, None
        finally:
            if connection and connection.is_connected():
                cursor.close()
                connection.close()

    def execute_insert(self, query, params):
        """Метод для безопасного добавления данных"""
        connection = None
        try:
            connection = mysql.connector.connect(**DB_CONFIG)
            cursor = connection.cursor()
            cursor.execute(query, params)
            connection.commit()
            return True
        except Error as e:
            messagebox.showerror("Ошибка добавления данных", f"Ошибка операции:\n{e}")
            return False
        finally:
            if connection and connection.is_connected():
                cursor.close()
                connection.close()

    def display_in_treeview(self, tree, headers, rows):
        """Очищает и наполняет переданную таблицу Treeview новыми структурами данных"""
        tree.delete(*tree.get_children())
        if headers is None or rows is None:
            return
        
        tree["columns"] = headers
        tree["show"] = "headings"
        
        for h in headers:
            tree.heading(h, text=h)
            tree.column(h, width=130, anchor="center")
            
        for row in rows:
            # Преобразование значений в читаемый строковый вид (для дат и логических полей)
            formatted_row = [str(item) if item is not None else "" for item in row]
            tree.insert("", "end", values=formatted_row)

    # --- ВКЛАДКА 1: АНАЛИТИЧЕСКИЕ ОТЧЕТЫ ЛАБОРАТОРНОЙ РАБОТЫ ---
    def init_reports_tab(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Аналитические отчеты")
        
        # Левая панель управления кнопками отчетов
        btn_panel = ttk.LabelFrame(tab, text="Выберите отчет", padding=10)
        btn_panel.pack(side='left', fill='y', padx=5, pady=5)
        
        ttk.Button(btn_panel, text="1. Действующие договоры", command=self.report_active_contracts).pack(fill='x', pady=5)
        ttk.Button(btn_panel, text="2. Начисления врачам", command=self.report_doctor_salaries).pack(fill='x', pady=5)
        
        # Компонент для ввода параметров 3-го отчета
        policy_frame = ttk.Frame(btn_panel)
        policy_frame.pack(fill='x', pady=5)
        ttk.Label(policy_frame, text="Полис для отчета №3:").pack(anchor='w')
        self.policy_entry = ttk.Entry(policy_frame)
        self.policy_entry.insert(0, "ОМС-1234567890")
        self.policy_entry.pack(fill='x', pady=2)
        ttk.Button(btn_panel, text="3. Поиск записей по полису", command=self.report_patient_appointments).pack(fill='x', pady=2)
        
        ttk.Button(btn_panel, text="4. Контроль сроков договоров", command=self.report_contract_control).pack(fill='x', pady=5)
        ttk.Button(btn_panel, text="5. Анализ прибыльности услуг", command=self.report_profitability).pack(fill='x', pady=5)
        
        # Правая панель вывода результатов в таблицу
        table_panel = ttk.LabelFrame(tab, text="Результаты выполнения", padding=5)
        table_panel.pack(side='right', fill='both', expand=True, padx=5, pady=5)
        
        # Скроллбары для таблицы отчетов
        scroll_y = ttk.Scrollbar(table_panel, orient="vertical")
        scroll_x = ttk.Scrollbar(table_panel, orient="horizontal")
        
        self.report_tree = ttk.Treeview(table_panel, yscrollcommand=scroll_y.set, xscrollcommand=scroll_x.set)
        scroll_y.config(command=self.report_tree.yview)
        scroll_x.config(command=self.report_tree.xview)
        
        scroll_y.pack(side='right', fill='y')
        scroll_x.pack(side='bottom', fill='x')
        self.report_tree.pack(fill='both', expand=True)

    # Реализация SQL логики для каждого отчета лабораторной работы
    def report_active_contracts(self):
        query = """
        SELECT в.фамилия, в.имя, в.отчество, в.телефон, тд.номер_договора, тд.дата_заключения, тд.срок_договора_лет 
        FROM врач в 
        JOIN  трудовой_договор тд ON в.id_врач = тд.id_врач 
        WHERE тд.договор_расторгнут = FALSE;
        """
        headers, rows = self.execute_query(query)
        self.display_in_treeview(self.report_tree, headers, rows)

    def report_doctor_salaries(self):
        query = """
        SELECT в.фамилия, в.имя, му.название AS услуга, 
               ROUND(му.зарплатный_фонд_руб / бригада_стат.кол_врачей, 2) AS начисление_врачу, 
               оу.фактическая_дата, 
               CASE WHEN оу.зарплата_начислена = FALSE THEN 'Ожидает' ELSE 'Начислено' END AS статус 
        FROM оказанная_услуга оу 
        JOIN медицинская_услуга му ON оу.id_услуги = му.id_услуги 
        JOIN запись_на_прием знп ON оу.id_записи = знп.id_записи 
        JOIN бригада ON бригада.id_услуги = му.id_услуги 
        JOIN врач в ON бригада.id_врач = в.id_врач 
        JOIN (
            SELECT id_услуги, COUNT(*) AS кол_врачей 
            FROM бригада 
            GROUP BY id_услуги
        ) AS бригада_стат ON му.id_услуги = бригада_стат.id_услуги 
        ORDER BY в.фамилия, оу.фактическая_дата;
        """
        headers, rows = self.execute_query(query)
        self.display_in_treeview(self.report_tree, headers, rows)

    def report_patient_appointments(self):
        policy = self.policy_entry.get().strip()
        query = """
        SELECT знп.номер_записи, му.название AS услуга, знп.дата_и_время_приема, знп.дата_создания_записи, 
               CASE WHEN знп.статус_выполнения = TRUE THEN 'Выполнен' ELSE 'Ожидает' END AS статус 
        FROM запись_на_прием знп 
        JOIN пациент п ON знп.id_пациента = п.id_пациента 
        JOIN медицинская_услуга му ON знп.id_услуги = му.id_услуги 
        WHERE п.лис_омс_дмс = %s;
        """
        headers, rows = self.execute_query(query, (policy,))
        self.display_in_treeview(self.report_tree, headers, rows)

    def report_contract_control(self):
        query = """
        SELECT в.фамилия, в.имя, тд.номер_договора, тд.дата_заключения, тд.срок_договора_лет, 
               DATE_ADD(тд.дата_заключения, INTERVAL тд.срок_договора_лет YEAR) AS дата_окончания, 
               CASE 
                   WHEN CURDATE() > DATE_ADD(тд.дата_заключения, INTERVAL  тд.срок_договора_лет YEAR) THEN 'ПРОСРОЧЕН' 
                   WHEN CURDATE() + INTERVAL 30 DAY > DATE_ADD(тд.дата_заключения, INTERVAL тд.срок_договора_лет YEAR) THEN 'Истекает скоро' 
                   ELSE 'Действует' 
               END AS контроль 
        FROM трудовой_договор тд 
        JOIN врач в ON тд.id_врач = в.id_врач 
        WHERE тд.договор_расторгнут = FALSE;
        """
        headers, rows = self.execute_query(query)
        self.display_in_treeview(self.report_tree, headers, rows)

    def report_profitability(self):
        query = """
        SELECT му.название, COUNT(оу.id_оказания) AS количество_оказаний, 
               му.цена_для_пациента_руб AS выручка_за_ед, му.себестоимость_руб AS расходники_за_ед, 
               му.зарплатный_фонд_руб AS фот_за_ед, 
               (му.цена_для_пациента_руб - му.себестоимость_руб - му.зарплатный_фонд_руб) AS прибыль_за_ед, 
               SUM(му.цена_для_пациента_руб - му.себестоимость_руб - му.зарплатный_фонд_руб) AS общая_прибыль 
        FROM оказанная_услуга оу 
        JOIN медицинская_услуга му ON оу.id_услуги = му.id_услуги 
        GROUP BY му.id_услуги 
        ORDER BY общая_прибыль DESC;
        """
        headers, rows = self.execute_query(query)
        self.display_in_treeview(self.report_tree, headers, rows)


    -- --- ВКЛАДКА 2: ИНТЕРАКТИВНЫЙ ПРОСМОТР ТАБЛИЦ ---
    def init_view_tables_tab(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Просмотр таблиц")
        
        top_panel = ttk.Frame(tab, padding=5)
        top_panel.pack(fill='x')
        
        ttk.Label(top_panel, text="Выберите таблицу для просмотра: ").pack(side='left', padx=5)
        
        self.table_selector = ttk.Combobox(top_panel, values=["врач", "трудовой_договор", "медицинская_услуга", "пациент", "запись_на_прием", "оказанная_услуга", "бригада"], state="readonly")
        self.table_selector.pack(side='left', padx=5)
        self.table_selector.current(0)
        
        ttk.Button(top_panel, text="Загрузить данные", command=self.load_selected_table).pack(side='left', padx=5)
        
        # Контейнер отображения структуры выбранной таблицы
        view_panel = ttk.Frame(tab, padding=5)
        view_panel.pack(fill='both', expand=True)
        
        scroll_y = ttk.Scrollbar(view_panel, orient="vertical")
        scroll_x = ttk.Scrollbar(view_panel, orient="horizontal")
        
        self.view_tree = ttk.Treeview(view_panel, yscrollcommand=scroll_y.set, xscrollcommand=scroll_x.set)
        scroll_y.config(command=self.view_tree.yview)
        scroll_x.config(command=self.view_tree.xview)
        
        scroll_y.pack(side='right', fill='y')
        scroll_x.pack(side='bottom', fill='x')
        self.view_tree.pack(fill='both', expand=True)
        
    def load_selected_table(self):
        table_name = self.table_selector.get()
        if not table_name:
            return
        query = f"SELECT * FROM {table_name};"
        headers, rows = self.execute_query(query)
        self.display_in_treeview(self.view_tree, headers, rows)


    -- --- ВКЛАДКА 3: УПРАВЛЕНИЕ И ДОБАВЛЕНИЕ ДАННЫХ ---
    def init_add_data_tab(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Добавление данных")
        
        # Форма ввода параметров нового врача
        doctor_frame = ttk.LabelFrame(tab, text="Регистрация нового врача", padding=10)
        doctor_frame.grid(row=0, column=0, padx=15, pady=15, sticky='nsew')
        
        fields = ["Паспорт:", "Фамилия:", "Имя:", "Отчество:", "Адрес:", "Телефон:"]
        self.doc_entries = {}
        for i, field in enumerate(fields):
            ttk.Label(doctor_frame, text=field).grid(row=i, column=0, sticky='w', pady=3)
            entry = ttk.Entry(doctor_frame, width=30)
            entry.grid(row=i, column=1, pady=3, padx=5)
            self.doc_entries[field] = entry
            
        ttk.Button(doctor_frame, text="Добавить врача", command=self.add_doctor).grid(row=len(fields), column=0, columnspan=2, pady=10)

        # Форма ввода параметров нового пациента
        patient_frame = ttk.LabelFrame(tab, text="Регистрация нового пациента", padding=10)
        patient_frame.grid(row=0, column=1, padx=15, pady=15, sticky='nsew')
        
        p_fields = ["Фамилия:", "Имя:", "Отчество:", "Адрес:", "Телефон:", "Дата рождения (ГГГГ-ММ-ДД):", "Полис ОМС/ДМС:"]
        self.pat_entries = {}
        for i, field in enumerate(p_fields):
            ttk.Label(patient_frame, text=field).grid(row=i, column=0, sticky='w', pady=3)
            entry = ttk.Entry(patient_frame, width=30)
            entry.grid(row=i, column=1, pady=3, padx=5)
            self.pat_entries[field] = entry
            
        ttk.Button(patient_frame, text="Добавить пациента", command=self.add_patient).grid(row=len(p_fields), column=0, columnspan=2, pady=10)

    def add_doctor(self):
        passport = self.doc_entries["Паспорт:"].get().strip()
        ln = self.doc_entries["Фамилия:"].get().strip()
        fn = self.doc_entries["Имя:"].get().strip()
        mn = self.doc_entries["Отчество:"].get().strip()
        adr = self.doc_entries["Адрес:"].get().strip()
        ph = self.doc_entries["Телефон:"].get().strip()
        
        if not passport or not ln or not fn:
            messagebox.showwarning("Внимание", "Поля Паспорт, Фамилия и Имя обязательны!")
            return
            
        query = "INSERT INTO врач (номер_паспорта, фамилия, имя, отчество, адрес, телефон) VALUES (%s, %s, %s, %s, %s, %s)"
        if self.execute_insert(query, (passport, ln, fn, mn, adr, ph)):
            messagebox.showinfo("Успех", f"Врач {ln} {fn} успешно добавлен в систему.")
            for entry in self.doc_entries.values():
                entry.delete(0, tk.END)

    def add_patient(self):
        ln = self.pat_entries["Фамилия:"].get().strip()
        fn = self.pat_entries["Имя:"].get().strip()
        mn = self.pat_entries["Отчество:"].get().strip()
        adr = self.pat_entries["Адрес:"].get().strip()
        ph = self.pat_entries["Телефон:"].get().strip()
        dob = self.pat_entries["Дата рождения (ГГГГ-ММ-ДД):"].get().strip()
        policy = self.pat_entries["Полис ОМС/ДМС:"].get().strip()
        
        if not ln or not fn or not policy:
            messagebox.showwarning("Внимание", "Поля Фамилия, Имя и Номер полиса обязательны!")
            return
            
        query = "INSERT INTO пациент (фамилия, имя, отчество, адрес, телефон, дата_рождения, лис_омс_дмс) VALUES (%s, %s, %s, %s, %s, %s, %s)"
        if self.execute_insert(query, (ln, fn, mn, adr, ph, dob if dob else None, policy)):
            messagebox.showinfo("Успех", f"Пациент {ln} {fn} успешно зарегистрирован.")
            for entry in self.pat_entries.values():
                entry.delete(0, tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = MedicalCenterApp(root)
    root.mainloop()