Files
ai-med-edu/collect_for_ai.py

242 lines
8.2 KiB
Python

#!/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()