diff --git a/src/cli.py b/src/cli.py index 080c950..6996e6b 100644 --- a/src/cli.py +++ b/src/cli.py @@ -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 diff --git a/src/generator.py b/src/generator.py index 8a71ff3..fc28722 100644 --- a/src/generator.py +++ b/src/generator.py @@ -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__": diff --git a/src/scripts/app_generator.py b/src/scripts/app_generator.py index 8d745ea..064cf6a 100644 --- a/src/scripts/app_generator.py +++ b/src/scripts/app_generator.py @@ -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 diff --git a/src/scripts/cicd_config_generator.py b/src/scripts/cicd_config_generator.py index 6a8ff68..a22ed9a 100644 --- a/src/scripts/cicd_config_generator.py +++ b/src/scripts/cicd_config_generator.py @@ -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 diff --git a/src/scripts/command_handler.py b/src/scripts/command_handler.py index 8a82540..7d862bb 100644 --- a/src/scripts/command_handler.py +++ b/src/scripts/command_handler.py @@ -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("") diff --git a/src/scripts/deployment_files_generator.py b/src/scripts/deployment_files_generator.py index 5f0c1bd..90f7d87 100644 --- a/src/scripts/deployment_files_generator.py +++ b/src/scripts/deployment_files_generator.py @@ -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 diff --git a/src/scripts/files.py b/src/scripts/files.py index 5a843ae..fe85789 100644 --- a/src/scripts/files.py +++ b/src/scripts/files.py @@ -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: diff --git a/src/scripts/input_handler.py b/src/scripts/input_handler.py index b0b63c0..5250c6f 100644 --- a/src/scripts/input_handler.py +++ b/src/scripts/input_handler.py @@ -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(",")] diff --git a/src/scripts/project.py b/src/scripts/project.py index 5714b1f..257d0dd 100644 --- a/src/scripts/project.py +++ b/src/scripts/project.py @@ -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 diff --git a/src/scripts/secretkey_generator.py b/src/scripts/secretkey_generator.py index 1af285c..6f72e14 100644 --- a/src/scripts/secretkey_generator.py +++ b/src/scripts/secretkey_generator.py @@ -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() diff --git a/src/scripts/settings.py b/src/scripts/settings.py index 048dd3b..4d2c683 100644 --- a/src/scripts/settings.py +++ b/src/scripts/settings.py @@ -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 diff --git a/src/scripts/template_engine.py b/src/scripts/template_engine.py index c4d491f..629687d 100644 --- a/src/scripts/template_engine.py +++ b/src/scripts/template_engine.py @@ -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.