257 lines
9.8 KiB
Python
257 lines
9.8 KiB
Python
import os
|
|
from pathlib import Path
|
|
import mimetypes
|
|
import markdown
|
|
|
|
# INPUT: Set your Application folder path here
|
|
APPLICATION_FOLDER = f'../Audio Transcription Editor' # Replace with your actual folder path
|
|
|
|
|
|
# Add these new variables
|
|
EXCLUDE_FOLDERS = {
|
|
'node_modules',
|
|
|
|
|
|
# 'node_modules',
|
|
# 'venv',
|
|
# 'env',
|
|
# '__pycache__',
|
|
# 'dist',
|
|
# 'build',
|
|
# '.pytest_cache'
|
|
}
|
|
|
|
EXCLUDE_FILES = {
|
|
'doc.py'
|
|
# '.DS_Store',
|
|
# 'Thumbs.db',
|
|
# 'package-lock.json'
|
|
}
|
|
|
|
# File extensions to exclude
|
|
EXCLUDE_EXTENSIONS = {
|
|
# '.pyc',
|
|
# '.pyo',
|
|
# '.log',
|
|
# '.tmp'
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def is_text_file(file_path):
|
|
"""Check if a file is likely a text/code file based on extension and mime type."""
|
|
# Common code file extensions
|
|
code_extensions = {
|
|
'.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.h', '.hpp',
|
|
'.cs', '.php', '.rb', '.go', '.rs', '.swift', '.kt', '.ts', '.jsx',
|
|
'.tsx', '.vue', '.scss', '.sass', '.less', '.sql', '.json', '.xml',
|
|
'.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf', '.sh', '.bat',
|
|
'.ps1', '.r', '.R', '.m', '.scala', '.clj', '.hs', '.elm', '.dart',
|
|
'.lua', '.pl', '.pm', '.tcl', '.awk', '.sed', '.dockerfile', '.md',
|
|
'.txt', '.log', '.gitignore', '.env', '.properties'
|
|
}
|
|
|
|
file_ext = Path(file_path).suffix.lower()
|
|
|
|
# Check by extension first
|
|
if file_ext in code_extensions:
|
|
return True
|
|
|
|
# Check by mime type for files without extension
|
|
if not file_ext:
|
|
mime_type, _ = mimetypes.guess_type(file_path)
|
|
if mime_type and mime_type.startswith('text/'):
|
|
return True
|
|
|
|
return False
|
|
|
|
def generate_tree_structure(root_path, prefix="", is_last=True, max_depth=None, current_depth=0):
|
|
"""Generate a tree-like directory structure."""
|
|
if max_depth is not None and current_depth > max_depth:
|
|
return ""
|
|
|
|
root = Path(root_path)
|
|
tree_str = ""
|
|
|
|
if current_depth == 0:
|
|
tree_str += f"{root.name}/\n"
|
|
|
|
try:
|
|
# Get all items and sort them (directories first, then files)
|
|
items = list(root.iterdir())
|
|
dirs = [item for item in items if item.is_dir() and not item.name.startswith('.') and item.name not in EXCLUDE_FOLDERS]
|
|
files = [item for item in items if item.is_file() and not item.name.startswith('.') and item.name not in EXCLUDE_FILES and item.suffix not in EXCLUDE_EXTENSIONS]
|
|
|
|
all_items = sorted(dirs) + sorted(files)
|
|
|
|
for i, item in enumerate(all_items):
|
|
is_last_item = i == len(all_items) - 1
|
|
|
|
if item.is_dir():
|
|
tree_str += f"{prefix}{'└── ' if is_last_item else '├── '}{item.name}/\n"
|
|
extension = " " if is_last_item else "│ "
|
|
tree_str += generate_tree_structure(
|
|
item,
|
|
prefix + extension,
|
|
is_last_item,
|
|
max_depth,
|
|
current_depth + 1
|
|
)
|
|
else:
|
|
tree_str += f"{prefix}{'└── ' if is_last_item else '├── '}{item.name}\n"
|
|
|
|
except PermissionError:
|
|
tree_str += f"{prefix}[Permission Denied]\n"
|
|
|
|
return tree_str
|
|
|
|
def generate_bash_command(root_folder):
|
|
"""Generate a bash command to recreate the directory and file structure."""
|
|
root_path = Path(root_folder)
|
|
dirs_to_create = []
|
|
files_to_create = []
|
|
|
|
for root, dirs, files in os.walk(root_folder, topdown=True):
|
|
# Skip hidden directories
|
|
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in EXCLUDE_FOLDERS]
|
|
|
|
for name in dirs:
|
|
dir_path = Path(root) / name
|
|
relative_dir = dir_path.relative_to(root_path)
|
|
dirs_to_create.append(f'"{relative_dir}"')
|
|
|
|
# Skip hidden files
|
|
files[:] = [f for f in files if not f.startswith('.') and f not in EXCLUDE_FILES and Path(f).suffix not in EXCLUDE_EXTENSIONS]
|
|
|
|
for name in files:
|
|
file_path = Path(root) / name
|
|
relative_file = file_path.relative_to(root_path)
|
|
files_to_create.append(f'"{relative_file}"')
|
|
|
|
command_parts = []
|
|
if dirs_to_create:
|
|
command_parts.append(f"mkdir -p {' '.join(dirs_to_create)}")
|
|
|
|
if files_to_create:
|
|
command_parts.append(f"touch {' '.join(files_to_create)}")
|
|
|
|
if not command_parts:
|
|
return "# No directories or files to create."
|
|
|
|
return " && ".join(command_parts)
|
|
|
|
def read_file_content(file_path):
|
|
"""Safely read file content with encoding detection."""
|
|
encodings = ['utf-8', 'utf-16', 'latin-1', 'cp1252']
|
|
|
|
for encoding in encodings:
|
|
try:
|
|
with open(file_path, 'r', encoding=encoding) as file:
|
|
return file.read()
|
|
except (UnicodeDecodeError, UnicodeError):
|
|
continue
|
|
except Exception as e:
|
|
return f"Error reading file: {str(e)}"
|
|
|
|
return "Unable to decode file content"
|
|
|
|
def get_language_from_extension(file_path):
|
|
"""Get the appropriate language identifier for markdown code blocks."""
|
|
ext = Path(file_path).suffix.lower()
|
|
language_map = {
|
|
'.py': 'python', '.js': 'javascript', '.ts': 'typescript', '.jsx': 'jsx',
|
|
'.tsx': 'tsx', '.html': 'html', '.css': 'css', '.scss': 'scss',
|
|
'.sass': 'sass', '.java': 'java', '.cpp': 'cpp', '.c': 'c', '.h': 'c',
|
|
'.hpp': 'cpp', '.cs': 'csharp', '.php': 'php', '.rb': 'ruby', '.go': 'go',
|
|
'.rs': 'rust', '.swift': 'swift', '.kt': 'kotlin', '.sql': 'sql',
|
|
'.json': 'json', '.xml': 'xml', '.yaml': 'yaml', '.yml': 'yaml',
|
|
'.toml': 'toml', '.sh': 'bash', '.bat': 'batch', '.ps1': 'powershell',
|
|
'.dockerfile': 'dockerfile', '.md': 'markdown', '.r': 'r', '.R': 'r',
|
|
'.scala': 'scala', '.clj': 'clojure', '.hs': 'haskell', '.lua': 'lua',
|
|
'.pl': 'perl', '.tcl': 'tcl',
|
|
}
|
|
|
|
return language_map.get(ext, 'text')
|
|
|
|
def generate_documentation(root_folder):
|
|
"""Generate complete markdown and HTML documentation for the project."""
|
|
root_path = Path(root_folder)
|
|
|
|
if not root_path.exists() or not root_path.is_dir():
|
|
print(f"Error: The folder '{root_folder}' does not exist or is not a directory.")
|
|
return
|
|
|
|
# Start building markdown content
|
|
markdown_content = [f"# {root_path.name} - Project Documentation\n"]
|
|
|
|
# Add project structure
|
|
markdown_content.append("## 📂 Project Structure\n")
|
|
markdown_content.append("```")
|
|
markdown_content.append(generate_tree_structure(root_path))
|
|
markdown_content.append("```\n")
|
|
|
|
# # Add bash command to recreate structure
|
|
# markdown_content.append("## ⚙️ Bash Command to Recreate Structure\n")
|
|
# markdown_content.append("```bash")
|
|
# markdown_content.append(generate_bash_command(root_path))
|
|
# markdown_content.append("```\n")
|
|
|
|
# Add files content
|
|
markdown_content.append("## 📄 Files Content\n")
|
|
|
|
for root, dirs, files in os.walk(root_folder):
|
|
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in EXCLUDE_FOLDERS]
|
|
for file in sorted(files):
|
|
if file.startswith('.') or file in EXCLUDE_FILES or Path(file).suffix in EXCLUDE_EXTENSIONS : continue
|
|
|
|
file_path = Path(root) / file
|
|
if is_text_file(file_path):
|
|
relative_path = file_path.relative_to(root_path)
|
|
markdown_content.append(f"### 📜 `{relative_path}`\n")
|
|
content = read_file_content(file_path)
|
|
language = get_language_from_extension(file_path)
|
|
markdown_content.append(f"```{language}\n{content}\n```\n")
|
|
|
|
final_markdown = '\n'.join(markdown_content)
|
|
|
|
# Write to markdown file
|
|
output_md_file = f"../{root_path.name}_documentation.md"
|
|
try:
|
|
with open(output_md_file, 'w', encoding='utf-8') as f:
|
|
f.write(final_markdown)
|
|
print(f"✅ Markdown documentation generated: {Path(output_md_file).resolve()}")
|
|
except Exception as e:
|
|
print(f"❌ Error writing markdown file: {str(e)}")
|
|
|
|
# Generate and write HTML file
|
|
html_template = """
|
|
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>{title}</title>
|
|
<style>
|
|
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; line-height: 1.6; padding: 2em; max-width: 1024px; margin: 0 auto; color: #333; }}
|
|
h1, h2, h3 {{ border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; }}
|
|
code {{ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; background-color: #f6f8fa; padding: 0.2em 0.4em; margin: 0; font-size: 85%; border-radius: 6px; }}
|
|
pre {{ background-color: #f6f8fa; padding: 16px; overflow: auto; border-radius: 6px; }}
|
|
pre code {{ padding: 0; margin: 0; font-size: 100%; background-color: transparent; border: none; }}
|
|
</style></head><body>{content}</body></html>
|
|
"""
|
|
html_content = markdown.markdown(final_markdown, extensions=['fenced_code', 'tables'])
|
|
final_html = html_template.format(title=f"{root_path.name} Documentation", content=html_content)
|
|
|
|
output_html_file = f"../{root_path.name}_documentation.html"
|
|
try:
|
|
with open(output_html_file, 'w', encoding='utf-8') as f:
|
|
f.write(final_html)
|
|
print(f"✅ HTML documentation generated: {Path(output_html_file).resolve()}")
|
|
except Exception as e:
|
|
print(f"❌ Error writing HTML file: {str(e)}")
|
|
|
|
# Main execution
|
|
if __name__ == "__main__":
|
|
if not APPLICATION_FOLDER or APPLICATION_FOLDER == "/path/to/your/Application":
|
|
print("⚠️ Please set the APPLICATION_FOLDER variable to your actual folder path.")
|
|
else:
|
|
print(f"🚀 Generating documentation for: {APPLICATION_FOLDER}")
|
|
generate_documentation(APPLICATION_FOLDER)
|