#!/usr/bin/env python3 """ Cross-platform structure generator for Wizamart project. Works on Windows, Linux, and macOS. Usage: python show_structure.py frontend python show_structure.py backend python show_structure.py tests python show_structure.py all """ import os import sys import subprocess from datetime import datetime from pathlib import Path from typing import List, Dict def count_files(directory: str, pattern: str) -> int: """Count files matching pattern in directory.""" if not os.path.exists(directory): return 0 count = 0 for root, dirs, files in os.walk(directory): # Skip __pycache__ and other cache directories dirs[:] = [d for d in dirs if d not in ['__pycache__', '.pytest_cache', '.git', 'node_modules']] for file in files: if pattern == '*' or file.endswith(pattern): count += 1 return count def get_tree_structure(directory: str, exclude_patterns: List[str] = None) -> str: """Generate tree structure for directory.""" if not os.path.exists(directory): return f"Directory {directory} not found" # Try to use system tree command first try: if sys.platform == 'win32': # Windows tree command result = subprocess.run( ['tree', '/F', '/A', directory], capture_output=True, text=True, encoding='utf-8', errors='replace' ) return result.stdout else: # Linux/Mac tree command with exclusions exclude_args = [] if exclude_patterns: exclude_args = ['-I', '|'.join(exclude_patterns)] result = subprocess.run( ['tree', '-F', '-a'] + exclude_args + [directory], capture_output=True, text=True ) if result.returncode == 0: return result.stdout except (subprocess.SubprocessError, FileNotFoundError): pass # Fallback: generate tree structure manually return generate_manual_tree(directory, exclude_patterns) def generate_manual_tree(directory: str, exclude_patterns: List[str] = None, prefix: str = "") -> str: """Generate tree structure manually when tree command is not available.""" if exclude_patterns is None: exclude_patterns = ['__pycache__', '.pytest_cache', '.git', 'node_modules', '*.pyc', '*.pyo'] output = [] path = Path(directory) try: items = sorted(path.iterdir(), key=lambda x: (not x.is_dir(), x.name)) for i, item in enumerate(items): # Skip excluded patterns skip = False for pattern in exclude_patterns: if pattern.startswith('*'): # File extension pattern if item.name.endswith(pattern[1:]): skip = True break else: # Directory or exact name pattern if item.name == pattern or pattern in str(item): skip = True break if skip: continue is_last = i == len(items) - 1 current_prefix = "└── " if is_last else "├── " if item.is_dir(): output.append(f"{prefix}{current_prefix}{item.name}/") extension = " " if is_last else "│ " subtree = generate_manual_tree(str(item), exclude_patterns, prefix + extension) if subtree: output.append(subtree) else: output.append(f"{prefix}{current_prefix}{item.name}") except PermissionError: output.append(f"{prefix}[Permission Denied]") return "\n".join(output) def generate_frontend_structure() -> str: """Generate frontend structure report.""" output = [] output.append("Frontend Folder Structure") output.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") output.append("=" * 78) output.append("") # Templates section output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ JINJA2 TEMPLATES ║") output.append("║ Location: app/templates ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append(get_tree_structure("app/templates")) # Static assets section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ STATIC ASSETS ║") output.append("║ Location: static ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append(get_tree_structure("static")) # Documentation section (if exists) if os.path.exists("docs"): output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ DOCUMENTATION ║") output.append("║ Location: docs ║") output.append("║ (also listed in tools structure) ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Note: Documentation is also included in tools structure") output.append(" for infrastructure/DevOps context.") # Statistics section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ STATISTICS ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Templates:") output.append(f" - Total HTML files: {count_files('app/templates', '.html')}") output.append(f" - Total Jinja2 files: {count_files('app/templates', '.j2')}") output.append("") output.append("Static Assets:") output.append(f" - JavaScript files: {count_files('static', '.js')}") output.append(f" - CSS files: {count_files('static', '.css')}") output.append(" - Image files:") for ext in ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico']: count = count_files('static', f'.{ext}') if count > 0: output.append(f" - .{ext}: {count}") if os.path.exists("docs"): output.append("") output.append("Documentation:") output.append(f" - Markdown files: {count_files('docs', '.md')}") output.append(f" - reStructuredText files: {count_files('docs', '.rst')}") output.append("") output.append("=" * 78) output.append("End of structure") return "\n".join(output) def generate_backend_structure() -> str: """Generate backend structure report.""" output = [] output.append("Backend Folder Structure") output.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") output.append("=" * 78) output.append("") exclude = ['__pycache__', '*.pyc', '*.pyo', '.pytest_cache', '*.egg-info', 'templates'] # Backend directories to include backend_dirs = [ ('app', 'Application Code'), ('middleware', 'Middleware Components'), ('models', 'Database Models'), ('storage', 'File Storage'), ('tasks', 'Background Tasks'), ('logs', 'Application Logs'), ] for directory, title in backend_dirs: if os.path.exists(directory): output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append(f"║ {title.upper().center(62)} ║") output.append(f"║ Location: {directory + '/'.ljust(51)} ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append(get_tree_structure(directory, exclude)) # Statistics section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ STATISTICS ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Python Files by Directory:") total_py_files = 0 for directory, title in backend_dirs: if os.path.exists(directory): count = count_files(directory, '.py') total_py_files += count output.append(f" - {directory}/: {count} files") output.append(f" - Total Python files: {total_py_files}") output.append("") output.append("Application Components (if in app/):") components = ['routes', 'services', 'schemas', 'exceptions', 'utils'] for component in components: component_path = f"app/{component}" if os.path.exists(component_path): count = count_files(component_path, '.py') output.append(f" - app/{component}: {count} files") output.append("") output.append("=" * 78) output.append("End of structure") return "\n".join(output) def generate_tools_structure() -> str: """Generate tools/infrastructure structure report.""" output = [] output.append("Tools & Infrastructure Structure") output.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") output.append("=" * 78) output.append("") exclude = ['__pycache__', '*.pyc', '*.pyo', '.pytest_cache', '*.egg-info'] # Tools directories to include tools_dirs = [ ('alembic', 'Database Migrations'), ('scripts', 'Utility Scripts'), ('docker', 'Docker Configuration'), ('docs', 'Documentation'), ] for directory, title in tools_dirs: if os.path.exists(directory): output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append(f"║ {title.upper().center(62)} ║") output.append(f"║ Location: {directory + '/'.ljust(51)} ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append(get_tree_structure(directory, exclude)) # Configuration files section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ CONFIGURATION FILES ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Root configuration files:") config_files = [ ('Makefile', 'Build automation'), ('requirements.txt', 'Python dependencies'), ('pyproject.toml', 'Python project config'), ('setup.py', 'Python setup script'), ('setup.cfg', 'Setup configuration'), ('alembic.ini', 'Alembic migrations config'), ('mkdocs.yml', 'MkDocs documentation config'), ('Dockerfile', 'Docker image definition'), ('docker-compose.yml', 'Docker services'), ('.dockerignore', 'Docker ignore patterns'), ('.gitignore', 'Git ignore patterns'), ('.env.example', 'Environment variables template'), ] for file, description in config_files: if os.path.exists(file): output.append(f" ✓ {file.ljust(25)} - {description}") # Statistics section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ STATISTICS ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Database Migrations:") if os.path.exists("alembic/versions"): migration_count = count_files("alembic/versions", ".py") output.append(f" - Total migrations: {migration_count}") if migration_count > 0: # Get first and last migration try: migrations = sorted([f for f in os.listdir("alembic/versions") if f.endswith('.py')]) if migrations: output.append(f" - First: {migrations[0][:40]}...") if len(migrations) > 1: output.append(f" - Latest: {migrations[-1][:40]}...") except Exception: pass else: output.append(" - No alembic/versions directory found") output.append("") output.append("Scripts:") if os.path.exists("scripts"): script_types = { '.py': 'Python scripts', '.sh': 'Shell scripts', '.bat': 'Batch scripts', } for ext, desc in script_types.items(): count = count_files("scripts", ext) if count > 0: output.append(f" - {desc}: {count}") else: output.append(" - No scripts directory found") output.append("") output.append("Documentation:") if os.path.exists("docs"): doc_types = { '.md': 'Markdown files', '.rst': 'reStructuredText files', } for ext, desc in doc_types.items(): count = count_files("docs", ext) if count > 0: output.append(f" - {desc}: {count}") else: output.append(" - No docs directory found") output.append("") output.append("Docker:") docker_files = ['Dockerfile', 'docker-compose.yml', '.dockerignore'] docker_exists = any(os.path.exists(f) for f in docker_files) if docker_exists: output.append(" ✓ Docker configuration present") if os.path.exists("docker"): output.append(f" - Docker directory files: {count_files('docker', '*')}") else: output.append(" - No Docker configuration found") output.append("") output.append("=" * 78) output.append("End of structure") return "\n".join(output) def generate_test_structure() -> str: """Generate test structure report.""" output = [] output.append("Test Folder Structure") output.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") output.append("=" * 78) output.append("") # Test files section output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ TEST FILES ║") output.append("║ Location: tests/ ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") exclude = ['__pycache__', '*.pyc', '*.pyo', '.pytest_cache', '*.egg-info'] output.append(get_tree_structure("tests", exclude)) # Configuration section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ TEST CONFIGURATION ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") output.append("Test configuration files:") test_config_files = ['pytest.ini', 'conftest.py', 'tests/conftest.py', '.coveragerc'] for file in test_config_files: if os.path.exists(file): output.append(f" ✓ {file}") # Statistics section output.append("") output.append("") output.append("╔══════════════════════════════════════════════════════════════════╗") output.append("║ STATISTICS ║") output.append("╚══════════════════════════════════════════════════════════════════╝") output.append("") # Count test files test_file_count = 0 if os.path.exists("tests"): for root, dirs, files in os.walk("tests"): dirs[:] = [d for d in dirs if d != '__pycache__'] for file in files: if file.startswith("test_") and file.endswith(".py"): test_file_count += 1 output.append("Test Files:") output.append(f" - Total test files: {test_file_count}") output.append("") output.append("By Category:") categories = ['unit', 'integration', 'system', 'e2e', 'performance'] for category in categories: category_path = f"tests/{category}" if os.path.exists(category_path): count = 0 for root, dirs, files in os.walk(category_path): dirs[:] = [d for d in dirs if d != '__pycache__'] for file in files: if file.startswith("test_") and file.endswith(".py"): count += 1 output.append(f" - tests/{category}: {count} files") # Count test functions test_function_count = 0 if os.path.exists("tests"): for root, dirs, files in os.walk("tests"): dirs[:] = [d for d in dirs if d != '__pycache__'] for file in files: if file.startswith("test_") and file.endswith(".py"): filepath = os.path.join(root, file) try: with open(filepath, 'r', encoding='utf-8') as f: for line in f: if line.strip().startswith("def test_"): test_function_count += 1 except Exception: pass output.append("") output.append("Test Functions:") output.append(f" - Total test functions: {test_function_count}") output.append("") output.append("Coverage Files:") if os.path.exists(".coverage"): output.append(" ✓ .coverage (coverage data file exists)") if os.path.exists("htmlcov"): output.append(" ✓ htmlcov/ (HTML coverage report exists)") output.append("") output.append("=" * 78) output.append("End of structure") return "\n".join(output) def main(): """Main entry point.""" if len(sys.argv) < 2: print("Usage: python show_structure.py [frontend|backend|tests|tools|all]") sys.exit(1) structure_type = sys.argv[1].lower() generators = { 'frontend': ('frontend-structure.txt', generate_frontend_structure), 'backend': ('backend-structure.txt', generate_backend_structure), 'tests': ('test-structure.txt', generate_test_structure), 'tools': ('tools-structure.txt', generate_tools_structure), } if structure_type == 'all': for name, (filename, generator) in generators.items(): print(f"\n{'=' * 60}") print(f"Generating {name} structure...") print('=' * 60) content = generator() with open(filename, 'w', encoding='utf-8') as f: f.write(content) print(f"✅ {name.capitalize()} structure saved to {filename}") print(f"\n{content}\n") elif structure_type in generators: filename, generator = generators[structure_type] print(f"Generating {structure_type} structure...") content = generator() with open(filename, 'w', encoding='utf-8') as f: f.write(content) print(f"\n✅ Structure saved to {filename}\n") print(content) else: print(f"Error: Unknown structure type '{structure_type}'") print("Valid options: frontend, backend, tests, tools, all") sys.exit(1) if __name__ == "__main__": main()