data seed feature for demo and prod

This commit is contained in:
2025-11-15 20:57:39 +01:00
parent 41439eed09
commit e3ed4a3295
17 changed files with 4574 additions and 1793 deletions

528
scripts/show_structure.py Normal file
View File

@@ -0,0 +1,528 @@
#!/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()