Files
FitTrack_GarminSync/examples/python_project_mapper.py

129 lines
4.1 KiB
Python
Executable File

import os
import ast
from pathlib import Path
def get_docstring(node):
"""Extract docstring from a node."""
return ast.get_docstring(node) or ""
def analyze_file(filepath):
"""Analyze a Python file and extract structure."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
tree = ast.parse(f.read())
except Exception as e:
return {'error': str(e)}
result = {
'classes': [],
'functions': [],
'imports': []
}
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef):
methods = []
for item in node.body:
if isinstance(item, ast.FunctionDef):
methods.append({
'name': item.name,
'docstring': get_docstring(item)
})
result['classes'].append({
'name': node.name,
'docstring': get_docstring(node),
'methods': methods
})
elif isinstance(node, ast.FunctionDef) and node.col_offset == 0:
# Only top-level functions
result['functions'].append({
'name': node.name,
'docstring': get_docstring(node)
})
return result
def generate_project_map(root_dir, output_file='project_map.txt', exclude_dirs=None):
"""Generate a project map for LLM consumption."""
if exclude_dirs is None:
exclude_dirs = {'__pycache__', '.git', 'venv', 'env', '.venv', 'node_modules', '.pytest_cache'}
root_path = Path(root_dir)
lines = []
lines.append("=" * 80)
lines.append(f"PROJECT MAP: {root_path.name}")
lines.append("=" * 80)
lines.append("")
# Collect all Python files
py_files = []
for root, dirs, files in os.walk(root_path):
# Remove excluded directories
dirs[:] = [d for d in dirs if d not in exclude_dirs]
for file in sorted(files):
if file.endswith('.py'):
py_files.append(Path(root) / file)
# Analyze each file
for filepath in sorted(py_files):
rel_path = filepath.relative_to(root_path)
lines.append(f"\n{'' * 80}")
lines.append(f"FILE: {rel_path}")
lines.append('' * 80)
analysis = analyze_file(filepath)
if 'error' in analysis:
lines.append(f" ⚠ Error parsing file: {analysis['error']}")
continue
# Classes
if analysis['classes']:
lines.append("\n CLASSES:")
for cls in analysis['classes']:
lines.append(f"{cls['name']}")
if cls['docstring']:
doc_preview = cls['docstring'].split('\n')[0][:60]
lines.append(f" └─ {doc_preview}")
if cls['methods']:
lines.append(f" Methods:")
for method in cls['methods']:
lines.append(f" - {method['name']}()")
# Functions
if analysis['functions']:
lines.append("\n FUNCTIONS:")
for func in analysis['functions']:
lines.append(f"{func['name']}()")
if func['docstring']:
doc_preview = func['docstring'].split('\n')[0][:60]
lines.append(f" └─ {doc_preview}")
if not analysis['classes'] and not analysis['functions']:
lines.append(" (No classes or functions found)")
# Write to file
output = '\n'.join(lines)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(output)
print(f"Project map generated: {output_file}")
print(f"Total files analyzed: {len(py_files)}")
return output
# Usage
if __name__ == "__main__":
# Change this to your project directory
project_dir = "."
# Generate the map
generate_project_map(project_dir, output_file="project_map.txt")
# Also print to console
with open("project_map.txt", 'r') as f:
print(f.read())