Files
orion/scripts/show_structure.py
Samir Boulahtit e9253fbd84 refactor: rename Wizamart to Orion across entire codebase
Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:46:56 +01:00

634 lines
24 KiB
Python

#!/usr/bin/env python3
"""
Cross-platform structure generator for Orion 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 subprocess
import sys
from datetime import datetime
from pathlib import Path
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
# 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, 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()