Initial commit: project structure, scripts and documentation
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user