Загрузка данных
import os
import re
import shutil
from pathlib import Path
# === НАСТРОЙКИ ===
# Сразу подставил твои пути с экрана, при необходимости поправь
SOURCE_DIR = Path(r"C:\Users\n.o.ishanhodzhaeva\Desktop\Forma\1 ТФ")
OUTPUT_DIR = Path(r"C:\Users\n.o.ishanhodzhaeva\Desktop\Forma\res")
YEAR = "2023"
# Список папок, которые нужно ОСТАВИТЬ
KEEP_FOLDERS = {
"мр и др": "МР и др",
"мр и др.": "МР и др",
"рпд": "РПД",
"рпп": "РПП"
}
# Ищет код направления в любой части названия папки (например, "22.03.02 Металлургия" или "1. 54.03.01 Дизайн")
OP_CODE_REGEX = re.compile(r"(\d{2}\.\d{2}\.\d{2})")
# Удаляет цифры в начале папок ("3 РПД" -> "РПД")
LEADING_NUM_REGEX = re.compile(r"^\d+[\.\s_-]*")
# Ищет "2022-2023", "2022_2023", "2022/2023" или просто "2022" в названиях файлов и папок
BAD_YEAR_REGEX = re.compile(r"2022(?:\s*[-_\/]\s*2023)?")
SYSTEM_FILES = {"thumbs.db", ".ds_store", "desktop.ini"}
def is_valid_file(file_path: Path) -> bool:
"""Проверяет, что файл не системный и не относится к 2022 / 2022-2023 году."""
name_lower = file_path.name.lower()
if name_lower in SYSTEM_FILES or name_lower.startswith("~$"):
return False
if BAD_YEAR_REGEX.search(name_lower):
return False
return True
def has_any_valid_files(dir_path: Path) -> bool:
"""Проверяет, есть ли внутри папки хотя бы один нормальный файл (защита от пустых папок)."""
for item in dir_path.rglob("*"):
if item.is_file():
# Если файл лежит внутри папки "2022-2023", игнорируем его
rel_parts = item.relative_to(dir_path).parts
if any(BAD_YEAR_REGEX.search(part.lower()) for part in rel_parts):
continue
if is_valid_file(item):
return True
return False
def clean_2022_items(profile_dir: Path):
"""Удаляет все папки и файлы, связанные с 2022 / 2022-2023 годом."""
# 1. Сначала сносим целые подпапки с 2022-2023 в названии
for item in list(profile_dir.rglob("*")):
if item.exists() and item.is_dir() and BAD_YEAR_REGEX.search(item.name.lower()):
shutil.rmtree(item)
print(f" [Удалена папка 2022] '{item.name}'")
# 2. Затем удаляем отдельные файлы с 2022-2023 и системный мусор
for item in list(profile_dir.rglob("*")):
if item.exists() and item.is_file():
if item.name.lower() in SYSTEM_FILES:
item.unlink()
elif BAD_YEAR_REGEX.search(item.name.lower()):
item.unlink()
print(f" [Удален файл 2022] '{item.name}'")
def move_file_safely(src_file: Path, target_dir: Path):
"""Безопасно перемещает файл, не перезаписывая существующие."""
target_path = target_dir / src_file.name
counter = 1
while target_path.exists():
target_path = target_dir / f"{src_file.stem}_{counter}{src_file.suffix}"
counter += 1
shutil.move(str(src_file), str(target_path))
def process_profile_content(profile_dir: Path):
"""Чистит 2022 год, оставляет МР и др, РПД, РПП, а остальные файлы выносит в корень."""
# Сначала вычищаем всё за 2022-2023 год
clean_2022_items(profile_dir)
for item in list(profile_dir.iterdir()):
if not item.is_dir():
continue
clean_name = LEADING_NUM_REGEX.sub("", item.name).strip()
clean_name_lower = clean_name.lower()
# 1. Если это одна из 3 папок (МР и др, РПД, РПП)
if clean_name_lower in KEEP_FOLDERS:
standard_name = KEEP_FOLDERS[clean_name_lower]
target_dir = profile_dir / standard_name
if item != target_dir:
if not target_dir.exists():
item.rename(target_dir)
print(f" [Переименована] '{item.name}' -> '{standard_name}'")
else:
for sub_item in item.iterdir():
if sub_item.is_file():
move_file_safely(sub_item, target_dir)
elif sub_item.is_dir():
shutil.move(str(sub_item), str(target_dir / sub_item.name))
shutil.rmtree(item)
print(f" [Объединена] '{item.name}' -> '{standard_name}'")
# 2. Все остальные папки — достаем файлы в корень и удаляем папку
else:
files_moved = 0
for sub_item in item.rglob("*"):
if sub_item.is_file():
move_file_safely(sub_item, profile_dir)
files_moved += 1
shutil.rmtree(item)
print(f" [Распакована и удалена] '{item.name}' (вынесено файлов: {files_moved})")
def main():
if not SOURCE_DIR.exists():
print(f"Ошибка: Исходная папка {SOURCE_DIR} не найдена!")
return
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
for root, dirs, _ in os.walk(SOURCE_DIR):
root_path = Path(root)
# Защита от захвата самой папки с результатами, если она лежит внутри SOURCE_DIR
if OUTPUT_DIR in root_path.parents or root_path == OUTPUT_DIR:
dirs.clear()
continue
match = OP_CODE_REGEX.search(root_path.name)
if match:
op_code = match.group(1)
for profile_dir in root_path.iterdir():
if not profile_dir.is_dir():
continue
# Пропускаем профиль, если он сам называется "2022-2023"
if BAD_YEAR_REGEX.search(profile_dir.name.lower()):
print(f"\n[Пропуск папки 2022] {profile_dir.name}")
continue
# ГЛАВНАЯ ПРОВЕРКА: если папка профиля пустая (или там только пустые папки/файлы 2022), пропускаем!
if not has_any_valid_files(profile_dir):
print(f"\n[Пропуск пустой папки] {op_code} -> {profile_dir.name}")
continue
profile_name = profile_dir.name.strip()
new_folder_name = f"{YEAR} {op_code} {profile_name}"
target_profile_path = OUTPUT_DIR / new_folder_name
print(f"\nОбработка: {new_folder_name}")
if target_profile_path.exists():
shutil.rmtree(target_profile_path)
shutil.copytree(profile_dir, target_profile_path)
process_profile_content(target_profile_path)
# Не идем вглубь профилей, чтобы не обрабатывать их внутренности как новые направления
dirs.clear()
print("\nГотово! Все папки собраны в:", OUTPUT_DIR)
if __name__ == "__main__":
main()