242 lines
8.2 KiB
Python
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() |