Загрузка данных
Работаем с запуском функционала окна Создание файлов для экспертов. Я даюто что должно быть сделано.
1. Пользователь выбирает из выпадающего списка группу
2. Нажмиает Добавить в список.
3. В поле Количество экспертов печатает количество. Сейчас почему то там постоянно стоит единица - ее надо убрать.
4. В поле Выбранные группы попадают выбранные в выпадающем списке группы. При желании или ошибкепользователь выбирает уже выбраннуюгруппу и кнопкой Удалить выбранное удаляет ее.
5. При нажатии Экспортировать в Эксель все группы создают эксель файл,где одна группа это один лист. Из показателей группы на листе создается матрица. Ее структура следующая:
Начиная с А1 с самого угла в первой строке идет шапка:
№п/п\ Наименование показателя\Код\
Далее таблица автоматически заполняется. В верху идет код показателей. С ячейки Д2 ставится автоматически единица по лиагонали вниз и вправо.
№п/п - понятно
Название показателя - полное название показателя из базы данных
Код - код показателя из базы данных.
Ниже высылаю код. Проверь так или не так здесь все это организовано. Если так то хорошо,если нет - то поправь. Созданный файл должен записываться по адресу dukora/excel/
Заголовок файла эксель должен состоять из слова Эксперт,подчеркивания и цифры 1,2,3 ....по количеству экспертов.
Не забывай,я вайбер.
# ============================================
# dukora/modules/experts/create_tab_inter.py
#=============================================
import customtkinter as ctk
from modules.experts.create_tab_work import CreateWorkController
class CreateExpertTab(ctk.CTkFrame):
def __init__(self, master):
super().__init__(master)
self.controller = CreateWorkController()
# Основной контейнер
main_container = ctk.CTkFrame(self, fg_color="transparent")
main_container.pack(fill="both", expand=True, padx=10, pady=10)
# --- СЕТКА ИНТЕРФЕЙСА ---
# Колонка 0: Заголовки (фиксированная ширина, выравнивание по левому краю)
main_container.grid_columnconfigure(0, weight=0, minsize=180)
# Колонка 1: Поля ввода (растягиваются)
main_container.grid_columnconfigure(1, weight=1)
# Колонка 2: Кнопка "Добавить"
main_container.grid_columnconfigure(2, weight=0, minsize=140)
# Строка 1: Выбор группы
lbl_group = ctk.CTkLabel(main_container, text="Выбрать группу:", anchor="w") # Текст внутри метки слева
lbl_group.grid(row=0, column=0, sticky="w", padx=(0, 5), pady=5) # Сама метка прижата к левому краю колонки
self.group_cb = ctk.CTkComboBox(main_container, state="readonly", width=200)
self.group_cb.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
btn_add_group = ctk.CTkButton(main_container, text="Добавить в список", command=self.add_to_list)
btn_add_group.grid(row=0, column=2, sticky="ew", padx=5, pady=5)
# Строка 2: Количество экспертов
lbl_count = ctk.CTkLabel(main_container, text="Количество экспертов:", anchor="w")
lbl_count.grid(row=1, column=0, sticky="w", padx=(0, 5), pady=5)
self.num_experts_entry = ctk.CTkEntry(main_container, justify="center", width=80)
self.num_experts_entry.insert(0, "1")
self.num_experts_entry.grid(row=1, column=1, sticky="ew", padx=5, pady=5)
# Строка 3+ : Выбранные группы (Текстовое поле занимает все 3 колонки)
list_lbl = ctk.CTkLabel(main_container, text="Выбранные группы:")
list_lbl.grid(row=2, column=0, columnspan=3, sticky="w", pady=(15, 2))
self.selected_text = ctk.CTkTextbox(main_container, height=120, width=540)
self.selected_text.grid(row=3, column=0, columnspan=3, sticky="ew", pady=(0, 15))
# --- НИЖНЯЯ ПАНЕЛЬ КНОПОК ---
btn_frame = ctk.CTkFrame(self, fg_color="transparent")
btn_frame.pack(fill="x", side="bottom", pady=10, padx=10)
del_btn = ctk.CTkButton(btn_frame, text="Удалить выбранное", fg_color="#d9534f", hover_color="#c9302c", command=self.remove_last, width=160)
del_btn.pack(side="left", expand=True, fill="x", padx=5)
export_btn = ctk.CTkButton(btn_frame, text="Экспортировать в Excel", command=self.export_all, width=180)
export_btn.pack(side="left", expand=True, fill="x", padx=5)
self.refresh_groups()
def refresh_groups(self):
self.controller.refresh_groups()
self.group_cb.configure(values=self.controller.groups)
if self.controller.groups:
self.group_cb.set(self.controller.groups[0])
def add_to_list(self):
group = self.group_cb.get().strip()
num_str = self.num_experts_entry.get().strip()
if not group or not num_str.isdigit():
ctk.CTkMessagebox.showwarning("Ошибка ввода", "Проверьте Группу и Количество.")
return
line = f"{group} (x{num_str})"
current = self.selected_text.get("1.0", "end").strip()
lines = [l.strip() for l in current.split('\n') if l.strip()]
if line not in lines:
self.selected_text.insert("end", line + "\n")
self.num_experts_entry.delete(0, "end")
self.num_experts_entry.insert(0, "1")
def remove_last(self):
content = self.selected_text.get("1.0", "end").strip().split("\n")
if not content or content == ['']:
return
content.pop(-1)
self.selected_text.delete("1.0", "end")
if content:
self.selected_text.insert("end", "\n".join(content) + "\n")
def export_all(self):
raw = self.selected_text.get("1.0", "end").strip()
if not raw:
ctk.CTkMessagebox.showwarning("Внимание", "Список групп пуст.", type="warning")
return
parsed = []
for line in raw.split("\n"):
if "(x" in line and ")" in line:
name = line[:line.rfind(" (x")]
count_str = line[line.rfind("x")+1:-1]
try:
count = int(count_str)
parsed.append({"name": name.strip(), "count": count})
except ValueError:
continue
success = self.controller.export_batch(parsed)
if success:
total = sum(x["count"] for x in parsed)
ctk.CTkMessagebox.showinfo("Готово", f"Создано {total} экспертных файлов.")
# =========================================
# dukora/modules/experts/create_tab_work.py
# =========================================
import os
from pathlib import Path
from openpyxl import Workbook
from modules.data.database_module import get_db_connection
def create_excel_template(groups_objects, output_filename):
wb = Workbook()
if "Sheet" in wb.sheetnames:
del wb["Sheet"]
created_any_sheet = False
for group_data in groups_objects:
indicators_list = group_data.get('indicators', [])
if not indicators_list:
continue
sheet_title = f"{group_data['name']}"[:31]
ws = wb.create_sheet(title=sheet_title)
created_any_sheet = True
headers = ["№ п/п", "Наименование показателя", "Код"]
for col_idx, header in enumerate(headers, start=1):
ws.cell(row=1, column=col_idx, value=header)
for idx, ind_tuple in enumerate(indicators_list, start=1):
name, code = ind_tuple[0], ind_tuple[1]
ws.cell(row=idx + 1, column=1, value=str(idx))
ws.cell(row=idx + 1, column=2, value=name)
ws.cell(row=idx + 1, column=3, value=code)
matrix_start_col = 4
matrix_start_row = 1
for col_offset, ind_tuple in enumerate(indicators_list):
code = ind_tuple[1]
ws.cell(row=matrix_start_row, column=matrix_start_col + col_offset, value=code)
size = len(indicators_list)
for r in range(size):
for c in range(size):
cell = ws.cell(row=matrix_start_row + r + 1, column=matrix_start_col + c)
cell.value = 1.0 if r == c else ""
if not created_any_sheet:
ws = wb.create_sheet(title="Пусто")
ws['A1'] = 'Нет индикаторов.'
project_root = Path(__file__).resolve().parents[3]
experts_folder = project_root / "experts"
os.makedirs(experts_folder, exist_ok=True)
full_path = experts_folder / output_filename
wb.save(full_path)
class CreateWorkController:
def __init__(self):
self.groups = []
self._gid_cache = {}
def refresh_groups(self):
conn = get_db_connection()
if not conn:
self.groups = ["Ошибка БД"]
return
cursor = conn.cursor()
cursor.execute("SELECT id, name FROM groups ORDER BY name")
rows = cursor.fetchall()
self.groups = [row[1] for row in rows]
self._gid_cache = {row[1]: row[0] for row in rows}
conn.close()
def export_batch(self, selected_groups_with_counts):
if not selected_groups_with_counts:
return False
conn = get_db_connection()
if not conn:
return False
cursor = conn.cursor()
enriched = []
for g in selected_groups_with_counts:
gid = self._gid_cache.get(g["name"])
if not gid:
continue
cursor.execute("""
SELECT name, code
FROM indicators
WHERE group_code IN (
SELECT code FROM groups WHERE id = ?
)
ORDER BY name
""", (gid,))
indicators = cursor.fetchall()
enriched.append({'name': g["name"], 'indicators': indicators})
conn.close()
if not enriched:
return False
base_success = True
expert_counter = 1
for item in selected_groups_with_counts:
for i in range(item["count"]):
filename = f"Эксперт_{expert_counter}.xlsx"
create_excel_template(enriched, filename)
expert_counter += 1
return base_success
# ================================================
# dukora/modules/data/database_module.py
# ================================================
import sys
import sqlite3
from pathlib import Path
try:
# Определяем корень проекта надежно:
# Если мы находимся внутри подпапок dukora, идем вверх до корня репозитория.
# __file__ дает путь к текущему .py файлу.
current_file_path = Path(__file__).resolve()
# Ищем папку 'dukora' в родителях
try:
dukora_parent = current_file_path.parents[[p.name for p in current_file_path.parents].index('dukora')]
except ValueError:
# Если по какой-то причине не нашли (запуск из другой директории), берем CWD
dukora_parent = Path.cwd()
BASE_DIR = dukora_parent
DB_FOLDER = BASE_DIR / "db"
ACTIVE_FLAG_PATH = DB_FOLDER / "active.flag"
DEFAULT_DB_PATH = DB_FOLDER / "database.db"
MASTERDATA_PATH = DB_FOLDER / "masterdata.db"
except Exception as e:
print(f"[FATAL] Не удалось вычислить путь к базе данных: {e}")
sys.exit(1)
def _determine_active_path():
try:
if ACTIVE_FLAG_PATH.exists():
with open(ACTIVE_FLAG_PATH, 'r', encoding='utf-8') as f:
name = f.read().strip()
if name == "masterdata":
return MASTERDATA_PATH
except Exception:
pass
return DEFAULT_DB_PATH
DB_PATH = _determine_active_path()
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session as SqlAlchemySession, declarative_base
engine = create_engine(f"sqlite:///{DB_PATH}", echo=False, connect_args={"check_same_thread": False})
Base = declarative_base()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
db_session = SessionLocal()
try:
yield db_session
finally:
db_session.close()
def get_db_connection():
active_path = _determine_active_path()
global DB_PATH
DB_PATH = active_path
# === КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ ===
# Сначала проверяем наличие папки и создаем её ДО попытки открыть БД
if not DB_FOLDER.exists():
print(f"[ACTION] Создаю папку для базы: {DB_FOLDER}")
try:
DB_FOLDER.mkdir(parents=True, exist_ok=True)
except PermissionError as pe:
print(f"[FATAL] Нет прав на создание папки '{DB_FOLDER}'. Запустите терминал от имени Администратора.")
raise SystemExit(1)
conn = sqlite3.connect(active_path)
conn.execute("PRAGMA foreign_keys = ON;")
return conn
def _create_tables(cursor):
cursor.execute('''
CREATE TABLE IF NOT EXISTS groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
code TEXT NOT NULL UNIQUE,
gain_group REAL DEFAULT 0.0
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS indicators (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
code TEXT NOT NULL UNIQUE,
group_code TEXT NOT NULL,
FOREIGN KEY (group_code) REFERENCES groups(code) ON DELETE CASCADE
)
''')
def _populate_test_data(conn):
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM groups")
count = cursor.fetchone()[0]
if count == 0:
print("[INFO] База данных пуста. Заполняю тестовыми данными...")
groups_data = [("Клубы", "KB", 1.5), ("Фанаты", "FAN", 2.0)]
cursor.executemany("INSERT INTO groups (name, code, gain_group) VALUES (?, ?, ?)", groups_data)
indicators_data = [
("ЦСКА", "KB-1", "KB"), ("Спартак", "KB-2", "KB"), ("Зенит", "KB-3", "KB"),
("Мужчины", "FAN-1", "FAN"), ("Женщины", "FAN-2", "FAN"), ("Дети", "FAN-3", "FAN")
]
cursor.executemany("INSERT INTO indicators (name, code, group_code) VALUES (?, ?, ?)", indicators_data)
conn.commit()
print("[SUCCESS] Тестовые данные успешно записаны.")
else:
print("[INFO] База данных уже содержит данные. Пропускаю инициализацию.")
def init_database():
print("[INIT] Проверка наличия рабочих баз данных...")
conn = None
try:
conn = get_db_connection()
cursor = conn.cursor()
_create_tables(cursor)
_populate_test_data(conn)
except sqlite3.Error as e:
print(f"[ERROR] Ошибка при инициализации DATABASE.DB: {e}")
if conn:
conn.rollback()
finally:
if conn:
conn.close()
master_conn = None
try:
if not MASTERDATA_PATH.exists():
print(f"[ACTION] База мастер-данных не найдена. Создаю новую: {MASTERDATA_PATH.name}")
master_conn = sqlite3.connect(MASTERDATA_PATH)
master_conn.execute("PRAGMA foreign_keys = ON;")
master_cursor = master_conn.cursor()
_create_tables(master_cursor)
_populate_test_data(master_conn)
except sqlite3.Error as e:
print(f"[ERROR] Ошибка при инициализации MASTERDATA.DB: {e}")
if master_conn:
master_conn.rollback()
finally:
if master_conn:
master_conn.close()
class Session:
def __init__(self):
pass
# --- ФУНКЦИИ ДЛЯ ИМПОРТА (лежат строго вне класса Session) ---
def list_groups():
"""Функция-заглушка для прямого импорта"""
return [(1, "Клубы"), (2, "Фанаты")]
def get_all_indicators_by_group(group_code):
"""Функция-заглушка для прямого импорта"""
mock_data = {
"KB": [(1, "ЦСКА"), (2, "Спартак"), (3, "Зенит")],
"FAN": [(4, "Мужчины"), (5, "Женщины"), (6, "Дети")]
}
return mock_data.get(group_code, [])
if __name__ == "__main__":
init_database()