#!/usr/bin/env python # -*- coding: utf-8 -*- """ Скрипт для сбора всех файлов проекта в один текстовый документ для последующей передачи в AI для генерации LaTeX-документации. """ import os import sys from pathlib import Path from datetime import datetime # ============================================================================ # НАСТРОЙКИ # ============================================================================ # Корневая директория проекта PROJECT_ROOT = Path(r"D:\ai-med-edu") # Файлы для исключения (бинарные, слишком большие, временные) EXCLUDE_EXTENSIONS = { '.pyc', '.pyo', '.pyd', '.exe', '.dll', '.so', '.dylib', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.dcm', '.nrrd', '.seg.nrrd', '.cache', '.pth', '.whl', '.mp4', '.avi', '.mov', '.mkv', '.mp3', '.wav', '.flac', '.psd', '.ai', '.eps', '.db', '.sqlite', '.sqlite3', } EXCLUDE_DIRS = { 'env', '__pycache__', '.git', '.idea', '.vscode', 'runs', 'predictions', 'output', 'masks_sam', 'images_result', 'dcm', # исключаем DICOM файлы (их много) } # Файлы, которые нужно включить обязательно (даже если в исключениях) FORCE_INCLUDE = { 'predefined_classes.txt', 'data.yaml', 'README.md', } # Максимальный размер файла для чтения (в байтах) - 1 МБ MAX_FILE_SIZE = 1024 * 1024 # Выходной файл OUTPUT_FILE = "project_files_for_ai.txt" # ============================================================================ # ФУНКЦИИ # ============================================================================ def get_file_tree(root: Path, prefix: str = "", level: int = 0, max_level: int = 5) -> str: """Генерирует ASCII-дерево файлов проекта.""" if level > max_level: return "" lines = [] try: items = sorted(root.iterdir(), key=lambda x: (not x.is_dir(), x.name)) except PermissionError: return f" [Permission denied: {root}]\n" for i, item in enumerate(items): is_last = i == len(items) - 1 connector = "└── " if is_last else "├── " # Проверяем, нужно ли исключать if item.is_dir(): if item.name in EXCLUDE_DIRS: lines.append(f"{prefix}{connector}{item.name}/ (excluded)") continue lines.append(f"{prefix}{connector}{item.name}/") extension = " " if is_last else "│ " lines.append(get_file_tree(item, prefix + extension, level + 1, max_level)) else: if item.suffix.lower() in EXCLUDE_EXTENSIONS and item.name not in FORCE_INCLUDE: lines.append(f"{prefix}{connector}{item.name} ({item.suffix} - excluded)") else: size = item.stat().st_size size_str = f" ({size:,} bytes)" if size < 1024*1024 else f" ({size/1024/1024:.1f} MB)" lines.append(f"{prefix}{connector}{item.name}{size_str}") return "\n".join(lines) def collect_files(root: Path) -> list: """Собирает список файлов для включения в отчёт.""" files = [] for file_path in root.rglob("*"): # Пропускаем директории if file_path.is_dir(): continue # Проверяем исключения rel_path = file_path.relative_to(root) # Пропускаем исключённые директории skip = False for part in rel_path.parts: if part in EXCLUDE_DIRS: skip = True break if skip: continue # Проверяем расширение if file_path.suffix.lower() in EXCLUDE_EXTENSIONS: if file_path.name in FORCE_INCLUDE: # Включаем даже если в исключениях pass else: continue # Проверяем размер try: size = file_path.stat().st_size if size > MAX_FILE_SIZE and file_path.name not in FORCE_INCLUDE: continue except OSError: continue files.append(file_path) return sorted(files) def read_file_content(file_path: Path) -> str: """Читает содержимое файла с обработкой кодировки.""" encodings = ['utf-8', 'cp1251', 'latin-1', 'utf-16'] for encoding in encodings: try: with open(file_path, 'r', encoding=encoding) as f: content = f.read() return content except (UnicodeDecodeError, UnicodeError): continue return "[ERROR: Cannot read file encoding]\n" def generate_report(files: list, tree: str) -> str: """Генерирует полный отчёт.""" lines = [] # Шапка lines.append("=" * 80) lines.append("СБОРКА ФАЙЛОВ ПРОЕКТА ДЛЯ AI") lines.append(f"Дата: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") lines.append(f"Корень проекта: {PROJECT_ROOT}") lines.append(f"Всего файлов: {len(files)}") lines.append("=" * 80) lines.append("") # Структура проекта lines.append("=" * 80) lines.append("СТРУКТУРА ПРОЕКТА (ДЕРЕВО)") lines.append("=" * 80) lines.append(tree) lines.append("") # Содержимое файлов lines.append("=" * 80) lines.append("СОДЕРЖИМОЕ ФАЙЛОВ") lines.append("=" * 80) lines.append("") for i, file_path in enumerate(files, 1): rel_path = file_path.relative_to(PROJECT_ROOT) lines.append("-" * 80) lines.append(f"[{i:3d}/{len(files)}] {rel_path}") lines.append("-" * 80) content = read_file_content(file_path) # Обрезаем слишком длинные файлы if len(content) > 50000: lines.append("[FOREIGN_BEGIN]") lines.append(content[:25000]) lines.append(f"... [TRUNCATED: {len(content)} chars] ...") lines.append(content[-25000:]) lines.append("[FOREIGN_END]") else: lines.append(content) lines.append("") return "\n".join(lines) # ============================================================================ # ОСНОВНАЯ ФУНКЦИЯ # ============================================================================ def main(): print("=" * 60) print("СБОРЩИК ФАЙЛОВ ПРОЕКТА ДЛЯ AI") print("=" * 60) print(f"Корень проекта: {PROJECT_ROOT}") print(f"Выходной файл: {OUTPUT_FILE}") print() if not PROJECT_ROOT.exists(): print(f"ОШИБКА: Папка {PROJECT_ROOT} не найдена!") sys.exit(1) print("1. Сканирование файлов...") files = collect_files(PROJECT_ROOT) print(f" Найдено файлов: {len(files)}") print("2. Генерация дерева...") tree = get_file_tree(PROJECT_ROOT, max_level=4) print("3. Сборка отчёта...") report = generate_report(files, tree) print("4. Сохранение...") output_path = PROJECT_ROOT / OUTPUT_FILE with open(output_path, 'w', encoding='utf-8') as f: f.write(report) print(f" Сохранено: {output_path}") print(f" Размер: {output_path.stat().st_size / 1024 / 1024:.2f} MB") print() print("ГОТОВО!") print(f"Файл {OUTPUT_FILE} создан в корне проекта.") print("Вы можете отправить его мне для анализа.") print("=" * 60) if __name__ == "__main__": main()