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 = """ {title} {content} """ 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)