refactor: remove try-except blocks from command handlers and generators for cleaner code

This commit is contained in:
sankalp committed 2025-10-29 11:27:42 +05:45
1 parent 0df1bf1224
commit 53376e13d6
12 files changed
+195 -273

No files matched your search

+2 -6
View File
@@ -105,9 +105,5 @@ class Cli:
return True
def generate_secret_keys(self) -> bool:
try:
generate_secret_command()
return True
except Exception as e:
UIFormatter.print_error(f"Failed to generate secret keys: {e}")
return False
generate_secret_command()
return True
+33 -42
View File
@@ -25,58 +25,49 @@ def clear_screen() -> None:
def main() -> None:
try:
# Parse command line arguments
args = parse_arguments()
# Parse command line arguments
args = parse_arguments()
# Handle secret command
if args.command == "secret":
handle_secret_command(args)
return
# Handle secret command
if args.command == "secret":
handle_secret_command(args)
return
# Handle app command
if args.command == "app":
handle_app_command(args)
return
# Handle app command
if args.command == "app":
handle_app_command(args)
return
# Default to setup command
# Clear screen
clear_screen()
# Default to setup command
# Clear screen
clear_screen()
# Display welcome screen
console.print()
console.print(UIFormatter.create_welcome_panel())
console.print()
# Display welcome screen
console.print()
console.print(UIFormatter.create_welcome_panel())
console.print()
# Get user input
project_dir, project_name, primary_app, app_names, metadata = get_user_input()
# Get user input
project_dir, project_name, primary_app, app_names, metadata = get_user_input()
# Confirm setup
if not confirm_setup(project_dir, project_name, app_names, metadata):
UIFormatter.print_info("Setup cancelled by user.")
return
# Confirm setup
if not confirm_setup(project_dir, project_name, app_names, metadata):
UIFormatter.print_info("Setup cancelled by user.")
return
# Run setup
console.print()
UIFormatter.print_info("Starting Django project setup...")
console.print()
# Run setup
console.print()
UIFormatter.print_info("Starting Django project setup...")
console.print()
django_cli = Cli(project_dir, project_name, primary_app, app_names, metadata)
success = django_cli.run_setup()
django_cli = Cli(project_dir, project_name, primary_app, app_names, metadata)
success = django_cli.run_setup()
# Display completion summary
UIFormatter.create_summary_panel(project_dir, project_name, app_names, True)
# Display completion summary
UIFormatter.create_summary_panel(project_dir, project_name, app_names, True)
# Exit with appropriate code
sys.exit(0 if success else 1)
except KeyboardInterrupt:
UIFormatter.print_info("\nSetup cancelled by user.")
sys.exit(0)
except Exception as e:
UIFormatter.print_error(f"Unexpected error: {str(e)}")
console.print("[dim]Please check your environment and try again.[/dim]")
sys.exit(1)
# Exit with appropriate code
sys.exit(0 if success else 1)
if __name__ == "__main__":
+10 -21
View File
@@ -65,21 +65,12 @@ class AppManager:
with change_cwd(apps_base_dir):
UIFormatter.print_info(f"Creating app '{self.app_name}' in directory: {os.getcwd()}")
try:
# Use django-admin instead of manage.py to avoid dependency issues
result = subprocess.run(
["django-admin", "startapp", self.app_name],
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
UIFormatter.print_error(f"Failed to create app '{self.app_name}': {e}")
if e.stderr:
UIFormatter.print_error(f"Error details: {e.stderr}")
if e.stdout:
UIFormatter.print_info(f"Output: {e.stdout}")
return False
result = subprocess.run(
["django-admin", "startapp", self.app_name],
capture_output=True,
text=True,
check=True
)
UIFormatter.print_success(f"Created Django app '{self.app_name}' in {apps_base_dir}")
return True
@@ -228,22 +219,20 @@ class AppManager:
# Create serializers.py
context = {"app_name": self.app_name}
serializers_content = template_engine.render_template("serializers.j2", context)
if not create_file_with_content(
create_file_with_content(
os.path.join(app_path, "serializers.py"),
serializers_content,
f"Created {self.app_name}/serializers.py",
should_format=True,
):
return False
)
# Create routes.py
routes_content = template_engine.render_template("routes.j2", context)
if not create_file_with_content(
create_file_with_content(
os.path.join(app_path, "routes.py"),
routes_content,
f"Created {self.app_name}/routes.py",
should_format=True,
):
return False
)
return True
+11 -12
View File
@@ -19,24 +19,23 @@ class CICDConfigGenerator:
github_dir = os.path.join(self.project_root, ".github", "workflows")
os.makedirs(github_dir, exist_ok=True)
context = {"project_name": self.project_name}
workflow_content = template_engine.render_template("github_actions_ci.j2", context)
workflow_file = os.path.join(github_dir, "ci.yml")
create_file_with_content(
workflow_file,
workflow_content,
f"Created Github Actions workflow ({workflow_file})",
)
context = {"project_name": self.project_name}
workflow_content = template_engine.render_template("github_actions_ci.j2", context)
workflow_file = os.path.join(github_dir, "ci.yml")
create_file_with_content(
workflow_file,
workflow_content,
f"Created Github Actions workflow ({workflow_file})",
)
return True
def create_gitlab_ci(self) -> bool:
with change_cwd(self.project_root):
context = {"project_name": self.project_name}
gitlab_ci_content = template_engine.render_template("gitlab_ci.j2", context)
config_file = ".gitlab-ci.yml"
create_file_with_content(
config_file,
".gitlab-ci.yml",
gitlab_ci_content,
f"Created GitLab CI configuration ({config_file})",
f"Created GitLab CI configuration (.gitlab-ci.yml)",
)
return True
return True
+55 -66
View File
@@ -33,80 +33,69 @@ def parse_arguments() -> argparse.Namespace:
def handle_secret_command(args: argparse.Namespace) -> None:
try:
from src.scripts.secretkey_generator import display_secret_keys, generate_multiple_keys
from src.scripts.secretkey_generator import display_secret_keys, generate_multiple_keys
# Generate keys
keys = generate_multiple_keys(args.count, args.length)
# Generate keys
keys = generate_multiple_keys(args.count, args.length)
# Display results
UIFormatter.print_info("")
display_secret_keys(keys)
# Display results
UIFormatter.print_info("")
display_secret_keys(keys)
# Show usage instructions
from rich.panel import Panel
from rich.text import Text
# Show usage instructions
from rich.panel import Panel
from rich.text import Text
instructions = Text()
instructions.append("📋 Usage Instructions:\n", style=UIColors.ACCENT)
instructions.append("1. Copy the appropriate secret key for your environment\n")
instructions.append("2. Add it to your .env file:\n")
instructions.append(" SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("3. Or set it as an environment variable:\n")
instructions.append(" export SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("4. Never commit secret keys to version control!\n", style=UIColors.WARNING)
instructions = Text()
instructions.append("📋 Usage Instructions:\n", style=UIColors.ACCENT)
instructions.append("1. Copy the appropriate secret key for your environment\n")
instructions.append("2. Add it to your .env file:\n")
instructions.append(" SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("3. Or set it as an environment variable:\n")
instructions.append(" export SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("4. Never commit secret keys to version control!\n", style=UIColors.WARNING)
console.print(Panel(instructions, title="💡 How to Use", border_style="blue"))
console.print()
except Exception as e:
UIFormatter.print_error(f"Failed to generate secret keys: {e}")
sys.exit(1)
console.print(Panel(instructions, title="💡 How to Use", border_style="blue"))
console.print()
def handle_app_command(args: argparse.Namespace) -> None:
try:
from src.scripts.app_generator import AppManager
from src.scripts.app_generator import AppManager
# Display welcome message
UIFormatter.print_info("")
UIFormatter.print_header("Django App Creation")
UIFormatter.print_info("")
# Display welcome message
UIFormatter.print_info("")
UIFormatter.print_header("Django App Creation")
UIFormatter.print_info("")
# Validate app name
is_valid, error_msg = validate_app_name(args.app_name)
if not is_valid:
UIFormatter.print_error(error_msg)
sys.exit(1)
# Create app manager
app_manager = AppManager(args.app_name)
# Create the app
success = app_manager.create_app()
if success:
UIFormatter.print_success(f"Django app '{args.app_name}' created successfully!")
# Show next steps
from rich.panel import Panel
from rich.text import Text
instructions = Text()
instructions.append("🚀 Next Steps:\n", style=UIColors.ACCENT)
instructions.append("1. The app has been added to INSTALLED_APPS in base.py\n")
instructions.append("2. Create your models in the app's models.py\n")
instructions.append("3. Run migrations: python manage.py makemigrations\n")
instructions.append("4. Apply migrations: python manage.py migrate\n")
instructions.append("5. Create views, serializers and routes(URLs) for your app\n")
console.print(Panel(instructions, title="💡 What's Next", border_style="green"))
else:
UIFormatter.print_error(f"Failed to create Django app '{args.app_name}'")
sys.exit(1)
UIFormatter.print_info("")
except Exception as e:
UIFormatter.print_error(f"Failed to create Django app: {e}")
# Validate app name
is_valid, error_msg = validate_app_name(args.app_name)
if not is_valid:
UIFormatter.print_error(error_msg)
sys.exit(1)
# Create app manager
app_manager = AppManager(args.app_name)
# Create the app
success = app_manager.create_app()
if not success:
UIFormatter.print_error(f"Failed to create Django app '{args.app_name}'")
sys.exit(1)
UIFormatter.print_success(f"Django app '{args.app_name}' created successfully!")
# Show next steps
from rich.panel import Panel
from rich.text import Text
instructions = Text()
instructions.append("🚀 Next Steps:\n", style=UIColors.ACCENT)
instructions.append("1. The app has been added to INSTALLED_APPS in base.py\n")
instructions.append("2. Create your models in the app's models.py\n")
instructions.append("3. Run migrations: python manage.py makemigrations\n")
instructions.append("4. Apply migrations: python manage.py migrate\n")
instructions.append("5. Create views, serializers and routes(URLs) for your app\n")
console.print(Panel(instructions, title="💡 What's Next", border_style="green"))
UIFormatter.print_info("")
+16 -19
View File
@@ -16,23 +16,23 @@ class DeploymentConfigGenerator:
with change_cwd(self.project_root):
context = {"project_name": self.project_name}
justfile_content = template_engine.render_template("justfile.j2", context)
result = create_file_with_content(
create_file_with_content(
"justfile",
justfile_content,
"Created justfile with Django development tasks",
)
return result
return True
def create_procfile(self) -> bool:
with change_cwd(self.project_root):
context = {"project_name": self.project_name}
procfile_content = template_engine.render_template("procfile.j2", context)
result = create_file_with_content(
create_file_with_content(
"Procfile",
procfile_content,
"Created Procfile with Gunicorn configuration",
)
return result
return True
def create_gunicorn_config(self) -> bool:
with change_cwd(self.project_root):
@@ -74,25 +74,23 @@ tmp_upload_dir = None
# certfile = "/path/to/cert.pem"
"""
config_file = "gunicorn_config.py"
result = create_file_with_content(
config_file,
create_file_with_content(
"gunicorn_config.py",
gunicorn_config_content,
f"Created {config_file} with Gunicorn configuration",
f"Created gunicorn_config.py with Gunicorn configuration",
)
return result
return True
def create_runtime_txt(self, python_version: str = "3.13") -> bool:
with change_cwd(self.project_root):
context = {"python_version": python_version}
runtime_content = template_engine.render_template("runtime_txt.j2", context)
runtime_file = "runtime.txt"
result = create_file_with_content(
runtime_file,
create_file_with_content(
"runtime.txt",
runtime_content,
f"Created {runtime_file} with Python version specification",
f"Created runtime.txt with Python version specification",
)
return result
return True
def create_nixpacks_toml(self) -> bool:
with change_cwd(self.project_root):
@@ -112,10 +110,9 @@ cmds = ["python manage.py collectstatic --noinput"]
cmd = "gunicorn {self.project_name}.wsgi:application --bind 0.0.0.0:$PORT"
"""
config_file = "nixpacks.toml"
result = create_file_with_content(
config_file,
create_file_with_content(
"nixpacks.toml",
nixpacks_content,
f"Created {config_file} for Nixpacks deployment",
f"Created nixpacks.toml for Nixpacks deployment",
)
return result
return True
+24 -41
View File
@@ -72,60 +72,43 @@ class FileManager:
"Created pyproject.toml file for uv",
)
def create_app_urls(self) -> bool:
apps_base_dir = self.project_root
def _get_apps_base_dir(self) -> str:
"""Get the base directory for apps based on project structure."""
if self.metadata.get("nested_apps") and self.metadata.get("nested_dir"):
apps_base_dir = os.path.join(self.project_root, self.metadata.get("nested_dir"))
return os.path.join(self.project_root, self.metadata.get("nested_dir"))
return self.project_root
def _create_app_file(self, apps_base_dir: str, app_name: str, template_name: str, filename: str) -> None:
"""Helper method to create app files."""
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
content = template_engine.render_template(template_name, context)
create_file_with_content(
filename,
content,
f"Created {app_name}/{filename}",
should_format=True,
)
def create_app_urls(self) -> bool:
apps_base_dir = self._get_apps_base_dir()
for app_name in self.app_names:
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
urls_content = template_engine.render_template("app_urls.j2", context)
create_file_with_content(
"urls.py",
urls_content,
f"Created {app_name}/urls.py",
should_format=True,
)
self._create_app_file(apps_base_dir, app_name, "app_urls.j2", "urls.py")
return True
def create_app_serializers(self) -> bool:
"""Create serializers.py file for each app."""
apps_base_dir = self.project_root
if self.metadata.get("nested_apps") and self.metadata.get("nested_dir"):
apps_base_dir = os.path.join(self.project_root, self.metadata.get("nested_dir"))
apps_base_dir = self._get_apps_base_dir()
for app_name in self.app_names:
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
serializers_content = template_engine.render_template("serializers.j2", context)
create_file_with_content(
"serializers.py",
serializers_content,
f"Created {app_name}/serializers.py",
should_format=True,
)
self._create_app_file(apps_base_dir, app_name, "serializers.j2", "serializers.py")
return True
def create_app_routes(self) -> bool:
"""Create routes.py file for each app."""
apps_base_dir = self.project_root
if self.metadata.get("nested_apps") and self.metadata.get("nested_dir"):
apps_base_dir = os.path.join(self.project_root, self.metadata.get("nested_dir"))
apps_base_dir = self._get_apps_base_dir()
for app_name in self.app_names:
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
routes_content = template_engine.render_template("routes.j2", context)
create_file_with_content(
"routes.py",
routes_content,
f"Created {app_name}/routes.py",
should_format=True,
)
self._create_app_file(apps_base_dir, app_name, "routes.j2", "routes.py")
return True
def update_project_urls(self) -> bool:
+17 -25
View File
@@ -112,35 +112,27 @@ class InputCollector:
console.print(f"[{UIColors.MUTED}]Please try again ({attempt}/{self.MAX_ATTEMPTS}).[/{UIColors.MUTED}]")
def get_app_names(self) -> list[str]:
try:
console.print(f"[{UIColors.HIGHLIGHT}]App Names[/{UIColors.HIGHLIGHT}]")
console.print(
f"[{UIColors.MUTED}]Enter app names separated by commas (no interactive prompts)[/{UIColors.MUTED}]"
)
console.print(f"[{UIColors.MUTED}]Example: users, products, orders[/{UIColors.MUTED}]")
console.print(f"[{UIColors.HIGHLIGHT}]App Names[/{UIColors.HIGHLIGHT}]")
console.print(
f"[{UIColors.MUTED}]Enter app names separated by commas (no interactive prompts)[/{UIColors.MUTED}]"
)
console.print(f"[{UIColors.MUTED}]Example: users, products, orders[/{UIColors.MUTED}]")
user_input = console.input(
f"[{UIColors.HIGHLIGHT}]Enter app names (comma-separated or single):[/{UIColors.HIGHLIGHT}] "
)
user_input = console.input(
f"[{UIColors.HIGHLIGHT}]Enter app names (comma-separated or single):[/{UIColors.HIGHLIGHT}] "
)
# Empty input - re-prompt (no interactive flow)
if not user_input.strip():
UIFormatter.print_error("At least one app name is required")
return self.get_app_names()
# Empty input - re-prompt (no interactive flow)
if not user_input.strip():
UIFormatter.print_error("At least one app name is required")
return self.get_app_names()
# Comma-separated input
if "," in user_input:
return self._parse_comma_separated_apps(user_input)
# Comma-separated input
if "," in user_input:
return self._parse_comma_separated_apps(user_input)
# Single app (no additional prompts)
return self._get_apps_starting_with(user_input.strip())
except KeyboardInterrupt:
UIFormatter.print_info("\nSetup cancelled by user.")
sys.exit(0)
except Exception as e:
UIFormatter.print_error(f"Unexpected error: {str(e)}")
return self.get_app_names() # Retry
# Single app (no additional prompts)
return self._get_apps_starting_with(user_input.strip())
def _parse_comma_separated_apps(self, user_input: str) -> list[str]:
app_list = [app.strip() for app in user_input.split(",")]
+17 -21
View File
@@ -51,20 +51,16 @@ class ProjectManager:
with change_cwd(apps_base_dir):
# Create each app
for app_name in self.app_names:
try:
subprocess.run(
[
sys.executable,
os.path.join(self.project_root, "manage.py"),
"startapp",
app_name,
],
check=True,
)
UIFormatter.print_success(f"Django app '{app_name}' created successfully!")
except subprocess.CalledProcessError as e:
UIFormatter.print_error(f"Failed to create app '{app_name}': {e}")
return False
subprocess.run(
[
sys.executable,
os.path.join(self.project_root, "manage.py"),
"startapp",
app_name,
],
check=True,
)
UIFormatter.print_success(f"Django app '{app_name}' created successfully!")
return True
@@ -106,11 +102,11 @@ class ProjectManager:
missing_files = [file_path for file_path in required_files if not os.path.exists(file_path)]
if missing_files:
UIFormatter.print_error("Project structure validation failed:")
for file_path in missing_files:
UIFormatter.print_error(f" Missing: {file_path}")
return False
if not missing_files:
UIFormatter.print_success("Project structure validation passed")
return True
UIFormatter.print_success("Project structure validation passed")
return True
UIFormatter.print_error("Project structure validation failed:")
for file_path in missing_files:
UIFormatter.print_error(f" Missing: {file_path}")
return False
+2 -6
View File
@@ -50,12 +50,8 @@ def generate_secret_command():
instructions.append("2. Add it to your .env file:\n")
instructions.append(" SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("3. Or set it as an environment variable:\n")
instructions.append(
" export SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE
)
instructions.append(
"4. Never commit secret keys to version control!\n", style=UIColors.WARNING
)
instructions.append(" export SECRET_KEY=your_secret_key_here\n", style=UIColors.CODE)
instructions.append("4. Never commit secret keys to version control!\n", style=UIColors.WARNING)
console.print(Panel(instructions, title="💡 How to Use", border_style="blue"))
console.print()
+6 -3
View File
@@ -51,33 +51,36 @@ class SettingsManager:
"app_names": effective_app_names,
}
base_content = template_engine.render_template("settings_base.j2", context)
return create_file_with_content(
create_file_with_content(
"base.py",
base_content,
"Updated settings/base.py with comprehensive configuration",
should_format=True,
)
return True
def update_development_settings(self) -> bool:
"""Update development.py with development-specific settings."""
with change_cwd(self.settings_folder):
context = {"secret_key": self.secret_key}
dev_content = template_engine.render_template("settings_development.j2", context)
return create_file_with_content(
create_file_with_content(
"development.py",
dev_content,
"Updated settings/development.py",
should_format=True,
)
return True
def update_production_settings(self) -> bool:
"""Update production.py with production-specific settings."""
with change_cwd(self.settings_folder):
context = {"use_database_url": self.metadata.get("use_database_url", True)}
prod_content = template_engine.render_template("settings_production.j2", context)
return create_file_with_content(
create_file_with_content(
"production.py",
prod_content,
"Updated settings/production.py",
should_format=True,
)
return True
+2 -11
View File
@@ -57,20 +57,11 @@ class TemplateEngine:
return template.render(**context)
def get_template_names(self) -> list[str]:
"""Get list of available template files.
Returns:
List of template file names
"""
"""Get list of available template files."""
if not os.path.exists(self.template_dir):
return []
template_files = []
for file in os.listdir(self.template_dir):
if file.endswith(".j2"):
template_files.append(file)
return sorted(template_files)
return sorted([file for file in os.listdir(self.template_dir) if file.endswith(".j2")])
def template_exists(self, template_name: str) -> bool:
"""Check if a template file exists.