что делает код поясни простым языком import os
import threading
from tkinter import *
from tkinter import ttk
from tkinter import filedialog as fd
from openpyxl import load_workbook
import pandas as pd
import configparser
from openpyxl import Workbook
from openpyxl.styles import PatternFill
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Pool
from tkinter.ttk import Progressbar
import numpy as np
from functools import partial
from pandas import isna  # Импортировать isna из pandas

config = configparser.ConfigParser()
firstTbl, secondTbl, outputTbl, outFolder = "", "", "", ""

if os.path.exists('config.ini'):
    config.read('config.ini', encoding='utf-8')
    firstTbl = config.get('Paths', 'first_table', fallback="")
    secondTbl = config.get('Paths', 'second_table', fallback="")
    outFolder = config.get('Paths', 'output_folder', fallback="")
    identifying_columns = config.get(
        'Columns', 'identifying_columns', fallback="").split(',')
    exception_words = config.get(
        'Settings', 'exception_words', fallback="").split(',')


def saveConfig():
    config['Paths'] = {'first_table': firstTbl,
                       'second_table': secondTbl, 'output_folder': outFolder}
    with open('config.ini', 'w', encoding='utf-8') as f:
        config.write(f)


def threaded_function(target_function, *args):
    threading.Thread(target=target_function, args=args).start()


def process_rows(identifying_values, df1, df2, all_columns):
    processed_cells = []

    row1 = df1[df1[identifying_columns].apply(
        tuple, axis=1) == identifying_values]
    row2 = df2[df2[identifying_columns].apply(
        tuple, axis=1) == identifying_values]

    for col in all_columns:
        fill = None

        value1 = row1[col].iloc[0] if col in df1.columns and not row1.empty else None
        value2 = row2[col].iloc[0] if col in df2.columns and not row2.empty else None

        if isna(value1) and isna(value2):
            continue

        if value1 is None and value2 is not None:
            fill = PatternFill(start_color="00FF00",
                               end_color="00FF00", fill_type="solid")
        elif value2 is None and value1 is not None:
            fill = PatternFill(start_color="FF0000",
                               end_color="FF0000", fill_type="solid")
        elif value1 != value2:
            fill = PatternFill(start_color="FFFF00",
                               end_color="FFFF00", fill_type="solid")
            value1 = f"{value1} --> {value2}"

        processed_cells.append(
            (col, value1 if value1 is not None else value2, fill))

    return row1.index[0] if not row1.empty else row2.index[0], processed_cells


def diffXlsx():
    df1 = pd.read_excel(firstTbl)
    df2 = pd.read_excel(secondTbl)

    all_columns = list(df1.columns) + \
        [col for col in df2.columns if col not in df1.columns]
    unique_identifying_values = set(df1[identifying_columns].apply(
        tuple, axis=1).unique()).union(df2[identifying_columns].apply(tuple, axis=1).unique())

    missing_in_df1 = [col for col in all_columns if col not in df1.columns]
    missing_in_df2 = [col for col in all_columns if col not in df2.columns]
    if missing_in_df1:
        print(
            f"Предупреждение: Столбцы, отсутствующие в первой таблице: {', '.join(missing_in_df1)}")
    if missing_in_df2:
        print(
            f"Предупреждение: Столбцы, отсутствующие во второй таблице: {', '.join(missing_in_df2)}")

    outputTbl = os.path.join(outFolder, os.path.basename(firstTbl))
    wb = Workbook()
    ws = wb.active
    ws.title = "Comparison"

    total_iterations = len(unique_identifying_values) * len(all_columns)
    func = partial(process_rows, df1=df1, df2=df2, all_columns=all_columns)

    for c_idx, col_name in enumerate(all_columns, 1):
        ws.cell(row=1, column=c_idx, value=col_name)

    processed_data = []
    with tqdm(total=total_iterations, desc="Processing") as pbar:
        for identifying_values in unique_identifying_values:
            original_idx, processed_cells = func(identifying_values)
            processed_data.append((original_idx, processed_cells))
            pbar.update(len(all_columns))

    processed_data.sort(key=lambda x: x[0])

    for r_idx, (original_idx, processed_cells) in enumerate(processed_data, 2):
        for col, value, fill in processed_cells:
            col_idx = all_columns.index(col) + 1
            cell = ws.cell(row=r_idx, column=col_idx, value=value)
            if fill:
                cell.fill = fill

    wb.save(outputTbl)


def splitPhoto():
    global firstTbl, outputTbl
    df = pd.read_excel(firstTbl)
    new_rows = []
    for index, row in df.iterrows():
        photos = row['photos']
        if isinstance(photos, str) and ',' in photos:
            for photo in photos.split(','):
                new_row = row.copy()
                new_row['photos'] = photo.strip()
                new_rows.append(new_row)
        else:
            new_rows.append(row)
    new_df = pd.DataFrame(new_rows)
    outputTbl = os.path.join(outFolder, os.path.basename(firstTbl))
    new_df.to_excel(outputTbl, index=False)


def splitCat():
    # добавляем exception_words в список глобальных переменных
    global firstTbl, outputTbl, exception_words
    df = pd.read_excel(firstTbl)
    new_rows = []
    for index, row in df.iterrows():
        cat = row['category']
        if isinstance(cat, str) and ',' in cat:
            for category in cat.split(','):
                category = category.strip()
                # проверка на исключение
                if category.lower() not in [word.lower() for word in exception_words]:
                    new_row = row.copy()
                    new_row['Номенклатура'] = category
                    new_rows.append(new_row)
        else:
            new_rows.append(row)
    new_df = pd.DataFrame(new_rows)
    outputTbl = os.path.join(outFolder, os.path.basename(firstTbl))
    new_df.to_excel(outputTbl, index=False)


def createDiffXlsxDeleteOld():
    global firstTbl, secondTbl, outFolder

    # Загрузка данных из файлов Excel
    df1 = pd.read_excel(firstTbl)
    df2 = pd.read_excel(secondTbl)

    # Находим уникальные ID, которые есть в обеих таблицах
    common_ids = pd.merge(df1, df2, on='id', how='inner')['id']

    # Удаляем строки из первой таблицы, которые имеют общие ID
    df1_unique = df1.loc[~df1['id'].isin(common_ids)]

    # Формирование пути для сохранения результата
    file_name = os.path.basename(firstTbl)
    output_table = os.path.join(outFolder, file_name)

    # Сохранение уникальных строк первой таблицы в новый файл Excel
    df1_unique.to_excel(output_table, index=False)


def createDiffXlsxDeleteOldDate():
    global firstTbl, secondTbl, outFolder

    # Загрузка данных из таблиц
    df1 = pd.read_excel(firstTbl)
    df2 = pd.read_excel(secondTbl)

    # Добавление столбца для сохранения порядка строк
    df1['original_index'] = df1.index
    df2['original_index'] = df2.index

    # Преобразование столбцов с датами в формат datetime
    df1['date'] = pd.to_datetime(df1['date'])
    df2['date'] = pd.to_datetime(df2['date'])

    # Объединение двух таблиц
    combined_df = pd.concat([df1, df2]).reset_index(drop=True)

    # Сортировка по uid, Ссылка фирмы, регион, name, author и датам
    combined_df.sort_values(['uid', 'Ссылка фирмы', 'регион', 'name', 'author', 'date'],
                            ascending=[True, True, True, True, True, False], inplace=True)

    # Удаление дубликатов, оставляя строки с наиболее актуальной датой
    combined_df.drop_duplicates(subset=[
                                'uid', 'Ссылка фирмы', 'регион', 'name', 'author'], keep='first', inplace=True)

    # Восстановление исходного порядка строк
    combined_df.sort_values('original_index', inplace=True)
    combined_df.drop('original_index', axis=1, inplace=True)

    # Преобразование даты в формат "07.06.2023"
    combined_df['date'] = combined_df['date'].dt.strftime('%d.%m.%Y')

    # Формирование пути для сохранения результата
    file_name = os.path.basename(firstTbl)
    output_table = os.path.join(outFolder, file_name)

    # Запись результата в файл Excel
    with pd.ExcelWriter(output_table, engine='openpyxl') as writer:
        combined_df.to_excel(writer, index=False, sheet_name='Sheet1')


def selectFirstTbl():
    global firstTbl
    firstTbl = fd.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
    firstTblLabel.config(text=firstTbl)
    saveConfig()


def selectSecondTbl():
    global secondTbl
    secondTbl = fd.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
    secondTblLabel.config(text=secondTbl)
    saveConfig()


def selectOutFolder():
    global outFolder
    outFolder = fd.askdirectory()
    outFolderLabel.config(text=outFolder)
    saveConfig()


if __name__ == '__main__':
    win = Tk()
    win.title("TablesExcel")
    win.geometry('300x300')

    Button(win, text="Выбрать первую таблицу",
           command=selectFirstTbl).pack(side=TOP)
    firstTblLabel = ttk.Label(text=firstTbl)
    firstTblLabel.pack()

    Button(win, text="Выбрать вторую таблицу",
           command=selectSecondTbl).pack(side=TOP)
    secondTblLabel = ttk.Label(text=secondTbl)
    secondTblLabel.pack()

    Button(win, text="Выбрать папку для сохранения",
           command=selectOutFolder).pack(side=TOP)
    outFolderLabel = ttk.Label(text=outFolder)
    outFolderLabel.pack()

    Button(win, text="Сравнение данных в Excel-файлах",
           command=diffXlsx).pack(side=TOP)
    Button(win, text="Обработка столбца 'Галерея'",
           command=splitPhoto).pack(side=TOP)
    Button(win, text="Обработка столбца 'Category'",
           command=splitCat).pack(side=TOP)
    Button(win, text="Удаление дубликатов фирм",
           command=createDiffXlsxDeleteOld).pack(side=TOP)
    Button(win, text="Удаление дубликатов комментариев",
           command=createDiffXlsxDeleteOldDate).pack(side=TOP)

    win.mainloop()