Загрузка данных
import os
import tkinter as tk
from tkinter import messagebox, ttk
# ------------------------------------
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Border, PatternFill, Protection, Side
from openpyxl.utils.dataframe import dataframe_to_rows
# --- ИСПРАВЛЕННЫЙ ИМПОРТ ---
# Теперь мы импортируем обе необходимые функции из базы данных
from modules.database_module import list_groups, list_indicators
def create_excel_template(groups_codes, output_filename):
"""
Функция создаёт шаблон Excel-файла для заданных групп и сохраняет его.
Теперь она берет значения ячеек из базы данных.
"""
def protect_and_format(ws, start_col, matrix_size):
blue_fill = PatternFill(
start_color="ADD8E6", end_color="ADD8E6", fill_type="solid"
)
green_fill = PatternFill(
start_color="90EE90", end_color="90EE90", fill_type="solid"
)
yellow_fill = PatternFill(
start_color="FFFF00", end_color="FFFF00", fill_type="solid"
)
protection_locked = Protection(locked=True)
protection_unlocked = Protection(locked=False)
ws.protection.enable()
for i in range(matrix_size):
for j in range(i + 1, matrix_size):
cell = ws.cell(row=i + 2, column=start_col + j)
cell.fill = blue_fill
cell.protection = protection_unlocked
for i in range(matrix_size):
for j in range(i):
cell = ws.cell(row=i + 2, column=start_col + j)
cell.fill = green_fill
cell.protection = protection_locked
for i in range(matrix_size):
cell = ws.cell(row=i + 2, column=start_col + i)
cell.fill = yellow_fill
cell.protection = protection_locked
def apply_borders(ws, start_col, matrix_size):
thin_border = Border(
left=Side(style="thin"),
right=Side(style="thin"),
top=Side(style="thin"),
bottom=Side(style="thin"),
)
for i in range(matrix_size):
for j in range(matrix_size):
cell = ws.cell(row=i + 2, column=start_col + j)
cell.border = thin_border
wb = Workbook()
all_groups = list_groups()
for group_code in groups_codes:
try:
group = next(g for g in all_groups if g.code == group_code)
indicators = list_indicators(group_code)
if not indicators:
continue
sheet_title = f"{group.name}"
ws = wb.create_sheet(title=sheet_title)
# --- ЗАГОЛОВКИ ТАБЛИЦЫ ---
ws.cell(row=1, column=1, value="№ п/п")
ws.cell(row=1, column=2, value="Показатель")
ws.cell(row=1, column=3, value="Код")
# --- ЗАПОЛНЯЕМ ТАБЛИЦУ ПОКАЗАТЕЛЕЙ ---
# Получаем список индикаторов один раз и сохраняем в переменную
indicators_list = list_indicators(group_code)
# Теперь работаем с сохраненным списком
for idx, ind in enumerate(indicators_list):
ws.cell(row=idx + 2, column=1, value=str(idx + 1))
ws.cell(row=idx + 2, column=2, value=ind.name)
ws.cell(row=idx + 2, column=3, value=ind.code)
# --- ПОДГОТОВКА К СОЗДАНИЮ МАТРИЦЫ ---
start_col = 4
matrix_start_row = 1
# Заголовки столбцов матрицы (коды показателей)
for col_idx, label in enumerate([ind.code for ind in indicators]):
ws.cell(row=matrix_start_row, column=start_col + col_idx, value=label)
matrix_size = len(indicators)
# --- СОЗДАЕМ СЛОВАРЬ ДЛЯ БЫСТРОГО ПОИСКА ЗНАЧЕНИЙ ИЗ БАЗЫ ---
# Ключ: (row_index, col_index), Значение: значение из базы
matrix_data_dict = {}
for ind in indicators:
for entry in ind.group.matrices:
# В базе индексы с 0, в Excel строки с 2 (из-за заголовков)
matrix_data_dict[(entry.row, entry.col)] = entry.value
# --- ЗАПОЛНЯЕМ ЯЧЕЙКИ МАТРИЦЫ ДАННЫМИ ИЗ БАЗЫ ---
for r_idx in range(matrix_size):
for c_idx in range(matrix_size):
# Ищем значение в нашем словаре
db_value = matrix_data_dict.get((r_idx, c_idx))
if db_value is not None:
# Если значение в базе есть (не None) — пишем его
ws.cell(
row=matrix_start_row + r_idx + 1,
column=start_col + c_idx,
value=float(db_value),
)
elif r_idx == c_idx:
# Если значения нет и это диагональ — пишем 1.0
ws.cell(
row=matrix_start_row + r_idx + 1,
column=start_col + c_idx,
value=1.0,
)
else:
# Если значения нет и это не диагональ — оставляем
# пустой
ws.cell(
row=matrix_start_row + r_idx + 1,
column=start_col + c_idx,
value="",
)
# Применяем стили и защиту
protect_and_format(ws, start_col, matrix_size)
apply_borders(ws, start_col, matrix_size)
except StopIteration:
pass
except Exception as e:
print(f"Ошибка при обработке группы '{group_code}': {e}")
# Удаляем стандартный пустой лист 'Sheet'
if "Sheet" in wb.sheetnames:
del wb["Sheet"]
# Сохраняем рабочую книгу
wb.save(output_filename)
class ExportMatrixWindow(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Создание файла для эксперта")
self.geometry("450x200")
main_frame = ttk.Frame(self)
main_frame.pack(padx=10, pady=10, fill="both", expand=True)
self.groups = []
self.selected_groups = []
self.num_experts = tk.IntVar()
tk.Label(main_frame, text="Группа:").grid(row=0, column=0, sticky="w")
self.group_var = tk.StringVar()
self.combobox = ttk.Combobox(
main_frame, textvariable=self.group_var, state="readonly"
)
self.combobox.grid(row=0, column=1, padx=(5, 10), sticky="ew")
self.refresh_groups()
add_group_btn = ttk.Button(
main_frame, text="Добавить группу", command=self.add_group
)
add_group_btn.grid(row=0, column=2, padx=(0, 10), sticky="e")
tk.Label(main_frame, text="Выбранные группы:").grid(row=1, column=0, sticky="w")
self.selected_groups_text = tk.Text(main_frame, width=30, height=4)
self.selected_groups_text.grid(
row=1, column=1, columnspan=2, pady=(5, 10), sticky="ew"
)
tk.Label(main_frame, text="Количество экспертов:").grid(
row=2, column=0, sticky="w"
)
self.experts_count_entry = ttk.Entry(main_frame, textvariable=self.num_experts)
self.experts_count_entry.grid(row=2, column=1, sticky="w")
remove_group_btn = ttk.Button(
main_frame, text="Удалить группу", command=self.remove_group
)
remove_group_btn.grid(row=2, column=2, sticky="e")
export_btn = ttk.Button(
main_frame, text="Экспортировать в Excel", command=self.export_to_excel
)
export_btn.grid(row=3, column=1, pady=(10, 0))
def refresh_groups(self):
self.groups = [grp.name for grp in list_groups()]
self.combobox.config(values=self.groups)
def add_group(self):
group_name = self.group_var.get()
if group_name and group_name.strip():
self.selected_groups.append(group_name)
self.selected_groups_text.insert(tk.END, f"{group_name}\n")
def remove_group(self):
if len(self.selected_groups) > 0:
last_group = self.selected_groups.pop(-1)
current_text = self.selected_groups_text.get("1.0", tk.END).strip()
updated_text = "\n".join(
line for line in current_text.split("\n") if line != last_group
)
self.selected_groups_text.delete("1.0", tk.END)
self.selected_groups_text.insert(tk.END, updated_text)
def export_to_excel(self):
"""Логика экспорта в Excel (ИСПРАВЛЕННАЯ ВЕРСИЯ)."""
# Получаем список названий групп из текстового поля
selected_group_names = (
self.selected_groups_text.get("1.0", tk.END).strip().split("\n")
)
# --- ЭТОТ БЛОК ТЕПЕРЬ ВЫПОЛНЯЕТСЯ ТОЛЬКО ОДИН РАЗ ---
# Составляем список кодов групп на основе выбранных названий
groups_codes = []
all_db_groups = list_groups()
for name in selected_group_names:
if name: # Проверяем, что строка не пустая
group = next((g for g in all_db_groups if g.name == name), None)
if group:
groups_codes.append(group.code)
# Если список пуст, выходим
if not groups_codes:
messagebox.showwarning("Предупреждение", "Не выбрано ни одной группы.")
return
# ----------------------------------------------------
# Получаем количество экспертов
try:
num_experts = int(self.num_experts.get())
if num_experts <= 0:
raise ValueError
except Exception:
messagebox.showerror(
"Ошибка", "Количество экспертов должно быть положительным числом."
)
return
# Каталог для сохранения файлов экспертов
experts_folder = os.path.join(os.path.dirname(__file__), "..", "experts")
os.makedirs(experts_folder, exist_ok=True)
# Генерируем файлы для каждого эксперта, используя уже готовый список
# groups_codes
for expert_num in range(1, num_experts + 1):
output_filename = os.path.join(experts_folder, f"Эксперт_{expert_num}.xlsx")
create_excel_template(groups_codes, output_filename)
messagebox.showinfo(
title="Экспорт завершён",
message=f"Успешно создано {num_experts} файлов экспертов.\n\nРасположение файлов:\n{experts_folder}",
)