Files
djinit/djinit/scripts/project.py
T

148 lines
6.3 KiB
Python

"""
Project management for djinit.
Handles creation of Django projects and apps.
"""
import os
from djinit.scripts.console_ui import UIFormatter
from djinit.scripts.django_helper import DjangoHelper
from djinit.utils import (
calculate_app_module_paths,
create_directory_with_init,
extract_existing_apps,
get_base_settings_path,
insert_apps_into_user_defined_apps,
read_base_settings,
)
class ProjectManager:
def __init__(self, project_dir: str, project_name: str, app_names: list, metadata: dict):
self.project_dir = project_dir
self.project_name = project_name
self.app_names = app_names
self.metadata = metadata
self.project_root = os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir)
# Support custom module name for the Django config package (e.g., 'config')
# Fall back to project_name if key present but None
self.module_name = metadata.get("project_module_name") or self.project_name
def create_project(self) -> bool:
os.makedirs(self.project_root, exist_ok=True)
unified = self.metadata.get("unified_structure", False)
success = DjangoHelper.startproject(self.module_name, self.project_root, unified=unified)
if success:
UIFormatter.print_success(f"Django project '{self.project_name}' created successfully!")
else:
UIFormatter.print_error(f"Failed to create Django project '{self.project_name}'")
return success
def _get_apps_base_dir(self) -> str:
"""Get the base directory for apps, considering nested structure."""
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"))
return apps_base_dir
def create_apps(self) -> bool:
apps_base_dir = self._get_apps_base_dir()
if apps_base_dir != self.project_root:
create_directory_with_init(apps_base_dir, f"Created {os.path.basename(apps_base_dir)}/__init__.py")
for app_name in self.app_names:
success = DjangoHelper.startapp(app_name, apps_base_dir)
if not success:
UIFormatter.print_error(f"Failed to create Django app '{app_name}'")
return False
UIFormatter.print_success(f"Django app '{app_name}' created successfully!")
# Add all apps to USER_DEFINED_APPS in settings
if not self.add_apps_to_settings():
return False
return True
def add_apps_to_settings(self) -> bool:
"""Add all apps to USER_DEFINED_APPS in base.py settings file."""
base_settings_path = get_base_settings_path(self.project_root, self.module_name)
if not os.path.exists(base_settings_path):
UIFormatter.print_error("Could not find base.py settings file")
return False
content = read_base_settings(self.project_root, self.module_name)
if content is None:
UIFormatter.print_error("Could not read base.py settings file")
return False
app_module_paths = calculate_app_module_paths(self.app_names, self.metadata)
existing_apps = extract_existing_apps(content)
apps_to_add = [app for app in app_module_paths if app not in existing_apps]
if not apps_to_add:
UIFormatter.print_success("All apps already configured in USER_DEFINED_APPS")
return True
updated_content = insert_apps_into_user_defined_apps(content, apps_to_add)
if not updated_content:
return False
with open(base_settings_path, "w") as f:
f.write(updated_content)
added_apps_str = ", ".join(apps_to_add)
UIFormatter.print_success(f"Added apps to USER_DEFINED_APPS: {added_apps_str}")
return True
def validate_project_structure(self) -> bool:
required_files = [
os.path.join(self.project_root, "manage.py"),
os.path.join(self.project_root, self.module_name, "__init__.py"),
os.path.join(self.project_root, self.module_name, "settings", "__init__.py"),
os.path.join(self.project_root, self.module_name, "settings", "base.py"),
os.path.join(self.project_root, self.module_name, "settings", "development.py"),
os.path.join(self.project_root, self.module_name, "settings", "production.py"),
os.path.join(self.project_root, self.module_name, "urls.py"),
os.path.join(self.project_root, self.module_name, "wsgi.py"),
os.path.join(self.project_root, self.module_name, "asgi.py"),
]
apps_base_dir = self._get_apps_base_dir()
# In predefined or unified structure, apps layout differs; skip strict per-app validation
if not self.metadata.get("predefined_structure") and not self.metadata.get("unified_structure"):
for app_name in self.app_names:
app_files = [
os.path.join(apps_base_dir, app_name, "__init__.py"),
os.path.join(apps_base_dir, app_name, "apps.py"),
os.path.join(apps_base_dir, app_name, "models.py"),
os.path.join(apps_base_dir, app_name, "views.py"),
os.path.join(apps_base_dir, app_name, "serializers.py"),
os.path.join(apps_base_dir, app_name, "routes.py"),
os.path.join(apps_base_dir, app_name, "tests.py"),
os.path.join(apps_base_dir, app_name, "migrations"),
os.path.join(apps_base_dir, app_name, "admin.py"),
]
required_files.extend(app_files)
missing_files = []
for file_path in required_files:
if file_path.endswith("settings"):
if not os.path.isdir(file_path):
missing_files.append(file_path)
elif not os.path.exists(file_path):
missing_files.append(file_path)
if not missing_files:
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