Загрузка данных
import os
import re
import shutil
from pathlib import Path
# === НАСТРОЙКИ ===
# Укажи путь к главной папке (например, где лежит "1 ТФ" или все выгрузки)
SOURCE_DIR = Path(r"C:\Путь\к\твоей\исходной\папке")
# Папка, куда сложатся готовые красивые папки (создастся автоматически)
OUTPUT_DIR = Path(r"C:\Путь\к\папке\Итог_2023")
YEAR = "2023"
# Регулярка для поиска папки направления по коду (например, "54.03.01 Дизайн")
OP_CODE_REGEX = re.compile(r"^(\d{2}\.\d{2}\.\d{2})\b")
# Регулярка для удаления цифр в начале папок ("1 Описание ОП" -> "Описание ОП")
LEADING_NUM_REGEX = re.compile(r"^\d+[\.\s_-]*")
def process_profile_Content(profile_dir: Path):
"""Обрабатывает внутренние папки профиля (1 Описание ОП, 3 РПД и т.д.)"""
for item in list(profile_dir.iterdir()):
if not item.is_dir():
continue
# Получаем список всех элементов внутри подпапки (игнорируя скрытые системные файлы)
contents = [
x for x in item.iterdir()
if x.name.lower() not in ("thumbs.db", ".ds_store", "desktop.ini")
]
files = [x for x in contents if x.is_file()]
subdirs = [x for x in contents if x.is_dir()]
# 1. Если папка вообще пустая — удаляем
if len(files) == 0 and len(subdirs) == 0:
item.rmdir()
print(f" [Удалена пустая папка] {item.name}")
# 2. Если внутри ровно 1 файл и нет подпапок — выносим файл в корень профиля, папку удаляем
elif len(files) == 1 and len(subdirs) == 0:
single_file = files[0]
target_path = profile_dir / single_file.name
# Защита от перезаписи, если файл с таким именем уже есть в корне
counter = 1
while target_path.exists():
target_path = profile_dir / f"{single_file.stem}_{counter}{single_file.suffix}"
counter += 1
shutil.move(str(single_file), str(target_path))
shutil.rmtree(item) # Удаляем опустевшую папку
print(f" [Файл вынесен] {single_file.name} (из '{item.name}')")
# 3. Если файлов больше одного (или есть подпапки) — убираем номер из названия папки
else:
clean_name = LEADING_NUM_REGEX.sub("", item.name).strip()
if clean_name and clean_name != item.name:
target_dir = profile_dir / clean_name
if not target_dir.exists():
item.rename(target_dir)
print(f" [Переименована] '{item.name}' -> '{clean_name}'")
else:
# Если папка с таким именем уже есть, переносим в нее файлы
for sub_item in item.iterdir():
shutil.move(str(sub_item), str(target_dir / sub_item.name))
shutil.rmtree(item)
print(f" [Объединена] '{item.name}' -> '{clean_name}'")
def main():
if not SOURCE_DIR.exists():
print(f"Ошибка: Исходная папка {SOURCE_DIR} не найдена!")
return
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# Ищем все папки в глубине, которые начинаются с кода ОП (например, 54.03.01)
for root, dirs, _ in os.walk(SOURCE_DIR):
root_path = Path(root)
match = OP_CODE_REGEX.match(root_path.name)
if match:
op_code = match.group(1) # Достаем только код, например "54.03.01"
# Внутри папки направления лежат папки профилей ("Транспортный и промышленный дизайн")
for profile_dir in root_path.iterdir():
if profile_dir.is_dir():
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, чтобы os.walk не шел глубже внутрь уже обработанных профилей
dirs.clear()
print("\nГотово! Все папки собраны в:", OUTPUT_DIR)
if __name__ == "__main__":
main()