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


# -*- coding: utf-8 -*-
# group_manager.py

import json
import logging
import os
import re
import tkinter as tk
from tkinter import messagebox, ttk

from modules.Export_Excel import create_excel_template
from modules.database_module import add_group, list_groups, Session, Group # Добавлены недостающие импорты

# --- ДОБАВЬТЕ ЭТУ СТРОКУ ---
CONFIG_FILE = "config.json"  # Укажите здесь реальное имя вашего файла настроек

# Если вы хотите хранить конфиг рядом со скриптом вне зависимости от папки запуска:
# CONFIG_FILE = os.path.join(os.path.dirname(__file__), "config.json")



# Загрузка и сохранение конфигурации
def load_config():
    global config_data
    if os.path.exists(CONFIG_FILE):
        logging.info(f"Файл конфигурации найден: {CONFIG_FILE}. Начинаем загрузку.")
        with open(CONFIG_FILE, "r") as file:
            try:
                config_data = json.load(file)
                logging.info("Файл конфигурации успешно загружен.")
                logging.info(f"Текущие данные: {config_data}")
            except json.JSONDecodeError:
                logging.error(f"Файл {CONFIG_FILE} поврежден или неправильный формат!")
                config_data = {"amplifier": "", "groups": {}}
    else:
        logging.info(
            f"Файл конфигурации не найден: {CONFIG_FILE}. Создание нового файла."
        )
        config_data = {"amplifier": "", "groups": {}}
        save_config()

def save_config():
    try:
        with open(CONFIG_FILE, "w") as file:
            json.dump(config_data, file)
        logging.info("Файл конфигурации успешно сохранён.")
    except Exception as e:
        logging.error(f"Ошибка при сохранении конфигурации: {e}")


# Класс менеджера групп
class GroupManager(tk.Toplevel):

    def __init__(self, parent):
        super().__init__(parent)
        self.title("Менеджер групп")
        self.geometry("450x350")
        # Сохраняем ссылку на главное окно
        self.parent = parent

        # Начало моего кода
        # Централизация окна относительно родительского окна
        self.update_idletasks()  # Обязательно вызывайте update_idletasks для точности расчетов
        parent_x = parent.winfo_x()
        parent_y = parent.winfo_y()
        parent_w = parent.winfo_width()
        parent_h = parent.winfo_height()

        # Рассчитываем позицию нового окна
        w = self.winfo_width()
        h = self.winfo_height()
        x = parent_x + (parent_w - w) // 2
        y = parent_y + (parent_h - h) // 2

        # Устанавливаем позицию окна
        self.geometry(f"+{x}+{y}")

        # Загружаем конфигурационные данные
        load_config()

        # Верхняя панель навигации
        nav_panel = tk.Frame(self)
        nav_panel.pack(fill="x", pady=10)

        # Название группы
        tk.Label(nav_panel, text="Название группы:").grid(
            row=0, column=0, sticky="w", padx=5
        )
        self.name_entry = tk.Entry(nav_panel, width=30)
        self.name_entry.grid(row=0, column=1, sticky="w", padx=5)

        # Код группы (уникальный идентификатор)
        tk.Label(nav_panel, text="Код группы (ID):").grid(
            row=1, column=0, sticky="w", padx=5
        )
        self.code_entry = tk.Entry(nav_panel, width=30)
        self.code_entry.grid(row=1, column=1, sticky="w", padx=5)

        # КОВ группы
        tk.Label(nav_panel, text="КОВ группы:").grid(
            row=2, column=0, sticky="w", padx=5
        )
        self.kov_entry = tk.Entry(nav_panel, width=15)
        self.kov_entry.grid(row=2, column=1, sticky="w", padx=5)

        # Постоянное поле для отображения коэффициента усиления
        tk.Label(nav_panel, text="Постоянный коэффициент усиления:").grid(
            row=3, column=0, sticky="w", padx=5
        )
        self.amplifier_display = tk.Entry(
            nav_panel, state="normal", readonlybackground="white", fg="black", width=15
        )
        self.amplifier_display.grid(row=3, column=1, sticky="w", padx=5)
        current_value = config_data.get("amplifier", "")
        self.amplifier_display.delete(
            0, tk.END
        )  # очистка поля перед установкой нового значения
        self.amplifier_display.insert(0, current_value)
        self.amplifier_display.config(
            state="readonly"
        )  # переключаем поле в режим "только для чтения"

        # Поле для временного ввода нового значения коэффициента
        tk.Label(nav_panel, text="Новый коэффициент усиления:").grid(
            row=4, column=0, sticky="w", padx=5
        )
        self.new_amplifier_entry = tk.Entry(nav_panel, width=15)
        self.new_amplifier_entry.grid(row=4, column=1, sticky="w", padx=5)

        # Кнопка для установки нового коэффициента
        apply_amp_button = tk.Button(
            nav_panel, text="Установить усиление", command=self.apply_new_amplifier
        )
        apply_amp_button.grid(row=5, column=0, columnspan=2, pady=10)

        # Кнопка для добавления группы
        add_group_button = tk.Button(
            nav_panel, text="Добавить группу", command=self.add_group
        )
        add_group_button.grid(row=6, column=0, columnspan=2, pady=10)

        # Дерево для отображения существующих групп
        self.groups_tree = ttk.Treeview(self, columns=("code", "name"), show="headings")
        self.groups_tree.column("code", width=150)
        self.groups_tree.column("name", width=150)
        self.groups_tree.heading("code", text="Код группы")
        self.groups_tree.heading("name", text="Название")
        self.groups_tree.pack(expand=True, fill="both")

        # Привязываем события к дереву
        self.groups_tree.bind("<Double-Button-1>", self.edit_group)
        self.groups_tree.bind("<Button-3>", self.show_context_menu)

        # Обновляем дерево с информацией о группах
        self.update_groups_tree()

    # Отдельно определяем методы класса
    def notify_main_window(self):
        # Уведомляем главное окно о том, что группа сменилась
        self.parent.notify_group_change()

    def select_group(self):
        # Вызываем уведомление главного окна о смене группы
        self.notify_main_window()

    def update_groups_tree(self):
        # Очищаем текущий список групп
        self.groups_tree.delete(*self.groups_tree.get_children())

        # Получаем список всех групп из базы данных
        groups = list_groups()

        # Заполняем таблицу заново
        for group in groups:
            self.groups_tree.insert("", "end", values=(group.code, group.name))

    def apply_new_amplifier(self):
        new_value = self.new_amplifier_entry.get().strip()
        if new_value:
            try:
                float(new_value)  # Проверяем, является ли значение числом
            except ValueError:
                messagebox.showwarning(
                    "Ошибка", "Коэффициент усиления должен быть числом."
                )
                return

            # Обновляем постоянное поле
            self.amplifier_display.config(state="normal")
            self.amplifier_display.delete(0, tk.END)
            self.amplifier_display.insert(0, new_value)
            self.amplifier_display.config(state="readonly")

            # Присваиваем значение глобальному состоянию
            config_data["amplifier"] = new_value
            save_config()

            # Очищаем временное поле ввода
            self.new_amplifier_entry.delete(0, tk.END)
        else:
            messagebox.showwarning(
                "Ошибка", "Нужно ввести значение коэффициента усиления."
            )
            
    def clear_input_fields(self):
        """Вынос очистки полей в отдельный метод для чистоты кода"""
        self.name_entry.delete(0, tk.END)
        self.code_entry.delete(0, tk.END)
        self.kov_entry.delete(0, tk.END)

    def add_group(self):
        name = self.name_entry.get().strip()
        code = self.code_entry.get().strip()
        kov_str = self.kov_entry.get().strip()

        # Проверка заполнения обязательных полей (до проверки формата числа)
        if not all([name, code]):
            return messagebox.showwarning("Ошибка", "Заполните название и код группы.")

        # Проверка формата КОВ (kov может быть пустым/None, поэтому проверяем только если строка не пустая)
        if kov_str and not re.match(r"^\d+(\.\d+)?$", kov_str):
            return messagebox.showwarning(
                "Ошибка", "КОВ должен быть численным значением."
            )
        
        try:
            kov = float(kov_str) if kov_str else None

            # Оптимизация: используем одну сессию для всех операций внутри метода
            from modules.database_module import Session, Group as DBGroup
            db_session = Session()
            
            # 1. Проверка уникальности кода в рамках текущей сессии
            is_duplicate = db_session.query(DBGroup.code).filter_by(code=code).first() is not None
            if is_duplicate:
                db_session.close()
                return messagebox.showwarning("Ошибка", "Такой код группы уже занят.")

            # 2. Вызов функции записи в базу 
            # (ваша функция add_group создаст свою внутреннюю сессию, это безопасно)
            add_group(name=name, code=code, kov=kov) 
            
            # Обновляем таблицу
            self.update_groups_tree()

            # Очищаем поля ввода
            self.clear_input_fields()

        except Exception as e:
            # Ловим любые непредвиденные ошибки (например, обрыв связи с БД или конфликт типов)
            messagebox.showerror("Критическая ошибка", f"Не удалось сохранить данные:\n{e}")
        finally:
            # Гарантируем закрытие нашей временной сессии, даже если произошла ошибка
            if 'db_session' in locals():
                db_session.close()
                

    def edit_group(self, event):
        selected_item = self.groups_tree.selection()
        if selected_item:
            item_values = self.groups_tree.item(selected_item)["values"]
            current_code = item_values[0]  # получаем код группы
            found_group = next(
                (grp for grp in list_groups() if grp.code == current_code), None
            )

            if found_group:
                # Начинаем создание окна редактирования группы
                dialog = tk.Toplevel(self)
                dialog.title("Редактировать группу")
                dialog.geometry("300x200")

                # ЦЕНТРИРУЕМ ОКНО ОТНОСИТЕЛЬНО РОДИТЕЛЯ
                self.update_idletasks()  # Важно для правильной оценки размеров
                parent_x = self.winfo_x()
                parent_y = self.winfo_y()
                parent_w = self.winfo_width()
                parent_h = self.winfo_height()

                # Расчёт позиций окна
                w = dialog.winfo_width()
                h = dialog.winfo_height()
                x = parent_x + (parent_w - w) // 2
                y = parent_y + (parent_h - h) // 2

                # Установка позиции окна
                dialog.geometry(f"+{x}+{y}")

                # Дальше остальная логика создания элементов окна...
                # Название группы
                tk.Label(dialog, text="Название группы:").pack(anchor="w")
                name_field = tk.Entry(dialog)
                name_field.insert(0, found_group.name)
                name_field.pack(fill="x", pady=5)

                # Код группы (уникальный идентификатор)
                tk.Label(dialog, text="Код группы (ID):").pack(anchor="w")
                code_field = tk.Entry(dialog)
                code_field.insert(0, found_group.code)
                code_field.pack(fill="x", pady=5)

                # КОВ группы
                tk.Label(dialog, text="КОВ группы:").pack(anchor="w")
                kov_field = tk.Entry(dialog)
                kov_field.insert(0, str(found_group.kov))
                kov_field.pack(fill="x", pady=5)

                # Кнопка для сохранения изменений
                save_btn = tk.Button(
                    dialog,
                    text="Сохранить",
                    command=lambda: self.save_group_changes(
                        current_code,
                        name_field.get(),
                        code_field.get(),
                        kov_field.get(),
                    ),
                )
                save_btn.pack(anchor="w", pady=5)
            else:
                messagebox.showwarning("Ошибка", "Не удалось найти указанную группу.")
        else:
            messagebox.showwarning("Ошибка", "Выберите группу для редактирования.")


    def save_group_changes(self, group_code, new_name, new_code, new_kov):
        # Проверяем оригинальную группу
        original_group = next(
            (grp for grp in list_groups() if grp.code == group_code), None
        )
        if original_group is None:
            return messagebox.showwarning("Ошибка", "Группа не найдена.")

        # Проверка уникальности KOV
        other_groups = [grp for grp in list_groups() if grp.code != group_code]
        existing_kovs = set(grp.kov for grp in other_groups)

        # Проверка нового значения KOV
        if new_kov.strip():
            try:
                new_kov_float = float(new_kov)
            except ValueError:
                return messagebox.showwarning(
                    "Ошибка", "Некорректное значение КОВ. Оно должно быть числом."
                )

            if new_kov_float in existing_kovs:
                return messagebox.showwarning(
                    "Ошибка", "Такое значение КОВ уже занято другой группой."
                )
        else:
            new_kov_float = None

        # Если код группы изменился, обновляем показатели
        old_code = original_group.code
        if new_code != old_code:
            # Получаем список показателей текущей группы
            indicators = get_all_indicators_by_group(old_code)
            updated_indicators = []

            # Формируем новый код для каждого показателя
            for ind in indicators:
                # ЗАМЕНА _ НА -
                parts = ind.code.split("-")
                if len(parts) > 1:
                    new_indicator_code = f"{new_code}-{parts[-1]}"
                else:
                    # Защита от ситуаций, когда в коде нет "-"
                    new_indicator_code = f"{new_code}-{ind.code}"
                updated_indicators.append((ind.id, new_indicator_code))

            # Обновляем коды показателей
            for ind_id, new_code in updated_indicators:
                print(f"Обновляю индикатор {ind_id}: новый код {new_code}")
                edit_indicator(ind_id, new_code=new_code)

        # Прямо обновляем группу в базе данных
        session = Session()
        group = session.query(Group).filter_by(code=group_code).first()
        if group:
            group.name = new_name
            group.code = new_code
            group.kov = new_kov_float
            session.commit()
            self.update_groups_tree()
            messagebox.showinfo("Готово", "Группа успешно обновлена.")
        else:
            messagebox.showwarning("Ошибка", "Группа не найдена.")

    def show_context_menu(self, event):
        context_menu = tk.Menu(self, tearoff=0)
        context_menu.add_command(label="Удалить группу", command=self.delete_group)
        context_menu.post(event.x_root, event.y_root)

    def delete_group(self):
        selected_item = self.groups_tree.selection()
        if not selected_item:
            messagebox.showwarning("Ошибка", "Нет выбранной группы для удаления.")
            return

        item_values = self.groups_tree.item(selected_item)["values"]
        group_code = item_values[0]
        found_group = next(
            (grp for grp in list_groups() if grp.code == group_code), None
        )

        if found_group:
            confirmation = messagebox.askyesno(
                "Подтверждение",
                f"Вы уверены, что хотите удалить группу '{found_group.name}'?",
            )
            if confirmation:
                with Session() as session:
                    group = session.query(Group).filter_by(code=group_code).first()
                    if group:
                        session.delete(group)
                        session.commit()
                        self.update_groups_tree()
                        messagebox.showinfo("Готово", "Группа успешно удалена.")
                    else:
                        messagebox.showwarning(
                            "Ошибка", "Группу не удалось найти в базе данных."
                        )
        else:
            messagebox.showwarning("Ошибка", "Не удалось найти указанную группу.")


# Основная точка входа
if __name__ == "__main__":
    root = tk.Tk()
    load_config()  # Загружаем конфигурацию
    manager = GroupManager(root)
    root.mainloop()