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


from pathlib import Path
import re
from openpyxl import load_workbook

# Имена файлов в папке с notebook.ipynb
SOURCE_FILE = Path("source.xlsx")       # Первая таблица
TARGET_FILE = Path("base.xlsx")          # Вторая таблица, исходная база
OUTPUT_FILE = Path("result_base.xlsx")   # Новый файл с результатом

SOURCE_SHEET = "Лист1"
TARGET_SHEET = "Вся База"


def normalize_identifier_from_need(col_m_value):
    """
    Сюда вставь свою фактическую логику преобразования M -> short_id.
    Пока пример: удалить пробелы и привести буквы к верхнему регистру.
    """
    if col_m_value is None:
        return None

    short_id = str(col_m_value).strip()

    if not short_id:
        return None

    short_id = re.sub(r"\s+", "", short_id).upper()

    return short_id


def as_text(value):
    """Сохраняет номера заявок/заданий как строки, включая ведущие нули."""
    if value is None:
        return ""
    return str(value).strip()


# Проверяем, что файлы реально видны из ноутбука
if not SOURCE_FILE.exists():
    raise FileNotFoundError(f"Не найден первый файл: {SOURCE_FILE.resolve()}")

if not TARGET_FILE.exists():
    raise FileNotFoundError(f"Не найден файл базы: {TARGET_FILE.resolve()}")


# Открываем первую книгу только для чтения.
wb_source = load_workbook(SOURCE_FILE, read_only=True, data_only=False)

# Открываем вторую книгу для внесения данных.
wb_target = load_workbook(TARGET_FILE)

ws_source = wb_source[SOURCE_SHEET]
ws_target = wb_target[TARGET_SHEET]


# Создаём быстрый словарь:
# идентификатор из AE второй книги -> номер строки во второй книге.
index_ae = {}

for row in range(2, ws_target.max_row + 1):
    value_ae = ws_target.cell(row=row, column=31).value  # AE = 31
    normalized_ae = normalize_identifier_from_need(value_ae)

    if normalized_ae:
        index_ae.setdefault(normalized_ae, []).append(row)


not_found = []
updated_rows = 0
duplicate_ids = []

# Идём по первой таблице:
# A = 1, B = 2, M = 13.
for source_row in range(2, ws_source.max_row + 1):
    request_number = as_text(ws_source.cell(source_row, column=1).value)  # A
    task_number = as_text(ws_source.cell(source_row, column=2).value)     # B
    value_m = ws_source.cell(source_row, column=13).value                 # M

    short_id = normalize_identifier_from_need(value_m)

    if not short_id:
        continue

    found_rows = index_ae.get(short_id)

    if not found_rows:
        not_found.append({
            "строка_лист1": source_row,
            "идентификатор_M": short_id
        })
        continue

    if len(found_rows) > 1:
        duplicate_ids.append({
            "строка_лист1": source_row,
            "идентификатор": short_id,
            "строки_в_базе": found_rows
        })

    # Если ID в AE встретился несколько раз,
    # переносим данные во все найденные строки.
    for target_row in found_rows:
        cell_af = ws_target.cell(row=target_row, column=32)  # AF = 32
        cell_ag = ws_target.cell(row=target_row, column=33)  # AG = 33

        # Принудительно задаём текстовый формат Excel.
        cell_af.number_format = "@"
        cell_ag.number_format = "@"

        cell_af.value = request_number
        cell_ag.value = task_number

        updated_rows += 1


# Сохраняем НЕ исходную базу, а новый файл.
wb_target.save(OUTPUT_FILE)

wb_source.close()
wb_target.close()

print(f"Готово: {OUTPUT_FILE.resolve()}")
print(f"Заполнено строк базы: {updated_rows}")
print(f"Не найдено идентификаторов: {len(not_found)}")
print(f"Идентификаторов с дублями в AE: {len(duplicate_ids)}")

if not_found:
    print("\nПервые 10 ID, которых нет в AE:")
    for item in not_found[:10]:
        print(item)

if duplicate_ids:
    print("\nПервые 10 ID, которые встретились в AE более одного раза:")
    for item in duplicate_ids[:10]:
        print(item)