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>
634 lines
24 KiB
Python
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()
|