import pandas as pd
import tkinter as tk
from tkinter import filedialog
import shutil
import re
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
from openpyxl.utils.dataframe import dataframe_to_rows
from concurrent.futures import ThreadPoolExecutor

def process_excel_file():
    # Открытие диалогового окна для выбора исходного файла
    root = tk.Tk()
    root.withdraw()
    input_file_path = filedialog.askopenfilename(title="Выберите исходный Excel файл", filetypes=[("Excel files", "*.xlsx;*.xls")])
    
    if not input_file_path:
        print("Файл не выбран.")
        return
    
    # Создание копии выбранного файла
    output_file_path = filedialog.asksaveasfilename(defaultextension=".xlsx", title="Сохраните обработанный файл", filetypes=[("Excel files", "*.xlsx;*.xls")])
    if not output_file_path:
        print("Местоположение для сохранения файла не выбрано.")
        return
    
    shutil.copy(input_file_path, output_file_path)
    
    # Чтение данных из Excel файла
    df = pd.read_excel(output_file_path)
    
    # Сохранение неизменённых данных (индексы 0-11)
    unchanged_data = df.iloc[:, :11].copy()
    
    # Обработка данных с индекса 11 и далее
    data = df.iloc[:, 11:]
    
    # Определение границ для разделения данных
    num_columns = len(data.columns)
    letter_index = next((i for i, col in enumerate(data.columns) if any(char.isalpha() for char in str(col))), num_columns)
    
    # Разделение данных на три части
    third = letter_index // 3
    abc_answers = data.iloc[:, :third].copy()
    correct_abc_answers = data.iloc[:, third:2*third].copy()
    binary_answers = data.iloc[:, 2*third:letter_index].copy()
    
    # Функция для назначения меток
    def assign_labels(indices, prefix):
        nonlocal question_counter
        for index in indices:
            question_labels[index] = f"{prefix}-{question_counter}"
            question_counter += 1
    
    # Извлечение и распознавание данных из оставшихся заголовков
    extra_headers = df.columns[letter_index:]
    parsed_headers = []
    binary_start_index = 11 + 2 * third
    
    question_labels = {}
    question_counter = 1
    current_index = binary_start_index + 1

    subject_question_info = {}
    category_scores = {}

    for header in extra_headers:
        lines = header.split('\n')
        if len(lines) == 4:
            subject = lines[0].strip()
            category = lines[1].strip()
            
            question_count_str = re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[2]).group() if re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[2]) else '0'
            score_per_question_str = re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[3]).group() if re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[3]) else '0'
            
            question_count = float(question_count_str.replace(',', '').replace('.', '').replace(',', '.')) if question_count_str.count(',') > 1 else float(question_count_str.replace(',', ''))
            score_per_question = float(score_per_question_str.replace(',', '').replace('.', '').replace(',', '.')) if score_per_question_str.count(',') > 1 else float(score_per_question_str.replace(',', ''))
            
            prefix = category[0].upper()
            indices = list(range(current_index, current_index + int(question_count)))
            assign_labels(indices, prefix)
            parsed_headers.append((subject, category, question_count, score_per_question, indices[0], indices[-1]))
            if subject not in subject_question_info:
                subject_question_info[subject] = {}
            subject_question_info[subject][category] = indices
            category_scores[(subject, category)] = score_per_question
            current_index += int(question_count)
    
    # Присвоение меток бинарным ответам
    binary_answers.columns = [question_labels.get(i, col) for i, col in enumerate(binary_answers.columns, start=binary_start_index + 1)]
    
    # Присвоение меток ABC ответам
    abc_answers.columns = binary_answers.columns.copy()

    # Создаем копию бинарных ответов для дальнейших вычислений
    numeric_df = binary_answers.copy()

    for subject, categories in subject_question_info.items():
        for category, indices in categories.items():
            original_col = f"{subject} {category} (Оригинал)"
            multiplied_col = f"{subject} {category} (Умноженный)"
            
            # Создание столбцов для оригинала и умноженного значений
            numeric_df[original_col] = numeric_df.loc[:, [question_labels[i] for i in indices]].sum(axis=1)
            numeric_df[multiplied_col] = numeric_df[original_col] * category_scores[(subject, category)]

    # Создание итоговых значений
    for subject, categories in subject_question_info.items():
        # Сумма оригиналов
        original_columns = [f"{subject} {category} (Оригинал)" for category in categories.keys()]
        numeric_df[f"{subject} Сумма (Оригинал)"] = numeric_df[original_columns].sum(axis=1)
        
        # Сумма умноженных значений
        multiplied_columns = [f"{subject} {category} (Умноженный)" for category in categories.keys()]
        numeric_df[f"{subject} Сумма (Умноженный)"] = numeric_df[multiplied_columns].sum(axis=1)
        
        # Процент
        numeric_df[f"{subject} Процент"] = (numeric_df[f"{subject} Сумма (Умноженный)"] / 40) * 100

    # Подсчет общего количества правильных ответов и общего балла
    numeric_df["Количество правильных ответов"] = numeric_df.filter(like="Сумма (Оригинал)").sum(axis=1)
    numeric_df["Общий балл"] = numeric_df.filter(like="Сумма (Умноженный)").sum(axis=1)
    
    # Рассчет общего процента
    num_subjects = len(subject_question_info)
    total_questions = num_subjects * 40
    numeric_df["Процент"] = (numeric_df["Общий балл"] / total_questions) * 100

    # Подсчет четных и нечетных ответов
    even_indices = [col for col in binary_answers.columns if int(re.search(r'\d+', col).group()) % 2 == 0]
    odd_indices = [col for col in binary_answers.columns if int(re.search(r'\d+', col).group()) % 2 != 0]

    numeric_df["Четные ответы"] = numeric_df[even_indices].sum(axis=1)
    numeric_df["Нечетные ответы"] = numeric_df[odd_indices].sum(axis=1)

    # Объединение обработанных данных
    processed_data = pd.concat([abc_answers, correct_abc_answers, numeric_df], axis=1)
    final_df = pd.concat([unchanged_data, processed_data], axis=1)

    # Сохранение результата в Excel
    final_df.to_excel(output_file_path, index=False)

    # Добавление новой операции: создание отдельных листов для каждого предмета и класса
    # Загрузка рабочего файла
    wb = load_workbook(output_file_path)

    # Получение информации о классах из столбца J (индекс 9)
    classes = df.iloc[:, 9].dropna().unique().astype(str)

    # Создание маппинга между метками вопросов и предметами
    question_label_to_subject = {}
    for subject, categories in subject_question_info.items():
        for category, indices in categories.items():
            for index in indices:
                q_label = question_labels.get(index)
                if q_label:
                    question_label_to_subject[q_label] = subject

    # Для каждого предмета и класса создаем листы и записываем данные
    for subject in subject_question_info.keys():
        # Получение соответствующих столбцов для предмета
        subject_cols = [col for col in binary_answers.columns if question_label_to_subject.get(col) == subject]
        
        for cls in classes:
            # Фильтрация данных по классу
            class_filter = df.iloc[:, 9].astype(str) == cls
            
            # Получение данных
            abc_data = abc_answers.loc[class_filter, subject_cols]
            binary_data = binary_answers.loc[class_filter, subject_cols]

            # Копирование первого столбца из df
            first_column_data = df.loc[class_filter, df.columns[0]]

            # Добавление первого столбца к abc_data и binary_data
            abc_data.insert(0, 'Первый столбец', first_column_data.values)
            binary_data.insert(0, 'Первый столбец', first_column_data.values)
            
            # Создание названий листов
            abc_sheet_name = f'Дистракторы_{cls}_{subject}'
            binary_sheet_name = f'БМ_{cls}_{subject}'
            
            # Удаление листов, если они уже существуют
            if abc_sheet_name in wb.sheetnames:
                del wb[abc_sheet_name]
            if binary_sheet_name in wb.sheetnames:
                del wb[binary_sheet_name]
            
            # Создание новых листов
            ws_abc = wb.create_sheet(title=abc_sheet_name)
            ws_bin = wb.create_sheet(title=binary_sheet_name)
            
            # Запись данных в листы
            for r_idx, row in enumerate(dataframe_to_rows(abc_data, index=False, header=True)):
                for c_idx, value in enumerate(row):
                    ws_abc.cell(row=r_idx+1, column=c_idx+1, value=value)
            
            for r_idx, row in enumerate(dataframe_to_rows(binary_data, index=False, header=True)):
                for c_idx, value in enumerate(row):
                    ws_bin.cell(row=r_idx+1, column=c_idx+1, value=value)

    # Окрашивание столбцов
    ws = wb.active

    colors = {
        "B-": "#1773c8",
        "Q-": "#ff7e25",
        "M-": "#ffc100",
        "Bilish": "#1773c8",
        "Qo‘llash": "#ff7e25",
        "Mulohaza": "#ffc100"
    }
    
    def color_columns(headers, color_dict):
        for col in ws.columns:
            col_letter = col[0].column_letter
            header = col[0].value
            if header:
                header = str(header).strip()
                for key, color in color_dict.items():
                    if key in header:
                        fill = PatternFill(start_color=color.replace("#", ""), end_color=color.replace("#", ""), fill_type="solid")
                        for cell in col:
                            cell.fill = fill
                        break
    
    # Запуск многопоточности для окрашивания столбцов
    with ThreadPoolExecutor() as executor:
        executor.submit(color_columns, binary_answers.columns, colors)
        executor.submit(color_columns, final_df.columns[letter_index:], colors)
    
    wb.save(output_file_path)
    print(f"Обработка завершена. Файл сохранён по пути: {output_file_path}")

process_excel_file()

надо еще создать новые листы под названием f'Карта решаемости_{cls}_{subject}'.
Для этого надо сделать копию листов f'БМ_{cls}_{subject}' и потом бинарные ответы надо умножить на значения из score_per_question_str исходя из category = lines[1].strip()
            
            question_count_str = re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[2]).group() if re.search(r'\d+(?:,\d{3})*(?:[.,]\d+)?', lines[2]) else '0'.

напиши полный код