Dev #32
No files matched your search
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Base classes for the djinit architecture.
|
||||
Defines the common interface for services, commands, and utilities.
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Any
|
||||
|
||||
|
||||
class BaseObject:
|
||||
"""
|
||||
Base object for all djinit classes.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class BaseService(BaseObject):
|
||||
"""
|
||||
Base class for all services.
|
||||
Services handle business logic and orchestration.
|
||||
"""
|
||||
|
||||
def __init__(self, project_root: str = None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.project_root = project_root
|
||||
|
||||
|
||||
class BaseCommand(BaseObject):
|
||||
"""
|
||||
Base class for all CLI commands.
|
||||
Commands handle user input and invoke services.
|
||||
"""
|
||||
|
||||
def __init__(self, args: Any, *args_list, **kwargs):
|
||||
super().__init__(*args_list, **kwargs)
|
||||
self.args = args
|
||||
|
||||
@abstractmethod
|
||||
def execute(self) -> None:
|
||||
"""Execute the command."""
|
||||
pass
|
||||
|
||||
|
||||
class BaseUtils(BaseObject):
|
||||
"""
|
||||
Base class for utility collections.
|
||||
Utilities provide helper functions.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -33,7 +33,7 @@ class InFileLogicParser:
|
||||
# Stack stores boolean results of nested IF blocks
|
||||
# Each element is (current_block_result, has_any_true_branch_executed)
|
||||
stack: List[List[bool]] = []
|
||||
|
||||
|
||||
lines = template_text.splitlines()
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
@@ -57,10 +57,10 @@ class InFileLogicParser:
|
||||
final_lines.append(line)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
|
||||
expr = stripped[9:].strip()
|
||||
current_stack = stack[-1]
|
||||
|
||||
|
||||
if current_stack[1]:
|
||||
current_stack[0] = False
|
||||
else:
|
||||
@@ -80,7 +80,7 @@ class InFileLogicParser:
|
||||
final_lines.append(line)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
|
||||
current_stack = stack[-1]
|
||||
if current_stack[1]:
|
||||
current_stack[0] = False
|
||||
@@ -121,7 +121,7 @@ class InFileLogicParser:
|
||||
iterable = eval(iterable_name, {"__builtins__": {}}, self.context)
|
||||
except Exception:
|
||||
iterable = []
|
||||
|
||||
|
||||
# Capture loop body
|
||||
loop_body = []
|
||||
i += 1
|
||||
@@ -131,12 +131,12 @@ class InFileLogicParser:
|
||||
loop_depth += 1
|
||||
elif lines[i].strip().startswith("# @ENDLOOP"):
|
||||
loop_depth -= 1
|
||||
|
||||
|
||||
if loop_depth > 0:
|
||||
loop_body.append(lines[i])
|
||||
i += 1
|
||||
|
||||
if i < len(lines): # Skip the @ENDLOOP line itself
|
||||
|
||||
if i < len(lines): # Skip the @ENDLOOP line itself
|
||||
i += 1
|
||||
|
||||
# Execute loop
|
||||
@@ -181,7 +181,7 @@ class InFileLogicParser:
|
||||
parts = rendered_line.split("# @IF ", 1)
|
||||
pre_content = parts[0].rstrip()
|
||||
rest = parts[1].strip()
|
||||
|
||||
|
||||
# Split rest into expression and post-content (if any)
|
||||
# This is tricky because the expression might have spaces.
|
||||
# If there's an # @ENDIF on the same line, use it.
|
||||
|
||||
+5
-3
@@ -8,7 +8,9 @@ import subprocess
|
||||
import sys
|
||||
|
||||
from djinit.cli import Cli
|
||||
from djinit.commands.handler import handle_app_command, handle_secret_command, parse_arguments
|
||||
from djinit.commands.app import AppCommand
|
||||
from djinit.commands.handler import parse_arguments
|
||||
from djinit.commands.secret import SecretCommand
|
||||
from djinit.ui.console import UIFormatter, console
|
||||
from djinit.ui.input import confirm_setup, get_user_input
|
||||
|
||||
@@ -27,11 +29,11 @@ def main() -> None:
|
||||
args = parse_arguments()
|
||||
|
||||
if args.command == "secret":
|
||||
handle_secret_command(args)
|
||||
SecretCommand(args).execute()
|
||||
return
|
||||
|
||||
if args.command == "app":
|
||||
handle_app_command(args)
|
||||
AppCommand(args).execute()
|
||||
return
|
||||
|
||||
clear_screen()
|
||||
|
||||
+38
-41
@@ -6,29 +6,19 @@ Handles creation of Django apps and updating settings.
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from djinit.core.base import BaseService
|
||||
from djinit.ui.console import UIFormatter
|
||||
from djinit.utils.common import (
|
||||
calculate_app_module_path,
|
||||
create_directory_with_init,
|
||||
create_file_from_template,
|
||||
create_init_file,
|
||||
detect_nested_structure_from_settings,
|
||||
extract_existing_apps,
|
||||
find_project_dir,
|
||||
find_settings_path,
|
||||
get_djinit_config,
|
||||
insert_apps_into_user_defined_apps,
|
||||
is_django_project,
|
||||
)
|
||||
from djinit.utils.common import CommonUtils
|
||||
from djinit.utils.django import DjangoHelper
|
||||
|
||||
|
||||
class AppManager:
|
||||
class AppManager(BaseService):
|
||||
def __init__(self, app_name: str):
|
||||
super().__init__()
|
||||
self.app_name = app_name
|
||||
self.current_dir = os.getcwd()
|
||||
self.manage_py_path = os.path.join(self.current_dir, "manage.py")
|
||||
self.config = get_djinit_config(self.current_dir)
|
||||
self.config = CommonUtils.get_djinit_config(self.current_dir)
|
||||
self._project_structure_cache = None
|
||||
|
||||
def create_app(self) -> bool:
|
||||
@@ -59,7 +49,7 @@ class AppManager:
|
||||
return True
|
||||
|
||||
def _is_django_project(self) -> bool:
|
||||
return is_django_project(self.current_dir)
|
||||
return CommonUtils.is_django_project(self.current_dir)
|
||||
|
||||
def _app_exists(self) -> bool:
|
||||
# For predefined structure, check in apps/ directory
|
||||
@@ -79,15 +69,15 @@ class AppManager:
|
||||
return self._create_predefined_app(os.path.join(self.current_dir, "apps"))
|
||||
|
||||
is_nested, nested_dir, apps_base_dir = self._get_project_structure()
|
||||
|
||||
|
||||
# Calculate app_module based on project structure
|
||||
if is_nested and nested_dir:
|
||||
# Normalize nested_dir (replace / with . for module path)
|
||||
module_base = nested_dir.replace(os.path.sep, '.')
|
||||
module_base = nested_dir.replace(os.path.sep, ".")
|
||||
app_module = f"{module_base}.{self.app_name}"
|
||||
else:
|
||||
app_module = self.app_name
|
||||
|
||||
|
||||
success = DjangoHelper.startapp(self.app_name, apps_base_dir, app_module)
|
||||
if success:
|
||||
UIFormatter.print_success(f"Created Django app '{self.app_name}' in {apps_base_dir}")
|
||||
@@ -96,7 +86,7 @@ class AppManager:
|
||||
return success
|
||||
|
||||
def _add_to_installed_apps(self) -> bool:
|
||||
settings_path = find_settings_path(self.current_dir)
|
||||
settings_path = CommonUtils.find_settings_path(self.current_dir)
|
||||
if not settings_path:
|
||||
UIFormatter.print_error("Could not find Django settings directory")
|
||||
return False
|
||||
@@ -113,30 +103,28 @@ class AppManager:
|
||||
app_module_path = f"apps.{self.app_name}"
|
||||
else:
|
||||
is_nested, nested_dir, _ = self._get_project_structure()
|
||||
app_module_path = calculate_app_module_path(self.app_name, is_nested, nested_dir)
|
||||
app_module_path = CommonUtils.calculate_app_module_path(self.app_name, is_nested, nested_dir)
|
||||
|
||||
# Use full AppConfig path to properly support nested apps
|
||||
# e.g. apps.users -> apps.users.apps.UsersConfig
|
||||
app_config_name = self.app_name.title().replace('_', '')
|
||||
app_config_name = self.app_name.title().replace("_", "")
|
||||
full_app_config = f"{app_module_path}.apps.{app_config_name}Config"
|
||||
|
||||
|
||||
# We'll check if either the module path or the full config path exists
|
||||
# but we'll prefer adding the full config path
|
||||
|
||||
existing_apps = extract_existing_apps(content)
|
||||
|
||||
existing_apps = CommonUtils.extract_existing_apps(content)
|
||||
|
||||
# Check if full config is already there
|
||||
if full_app_config in existing_apps:
|
||||
UIFormatter.print_success(f"App '{full_app_config}' already configured in USER_DEFINED_APPS")
|
||||
return True
|
||||
|
||||
|
||||
# Check if legacy module path is there, if so, upgrade it
|
||||
if app_module_path in existing_apps:
|
||||
from djinit.utils.common import replace_app_in_user_defined_apps
|
||||
|
||||
UIFormatter.print_info(f"Upgrading app configuration from '{app_module_path}' to '{full_app_config}'...")
|
||||
updated_content = replace_app_in_user_defined_apps(content, app_module_path, full_app_config)
|
||||
|
||||
updated_content = CommonUtils.replace_app_in_user_defined_apps(content, app_module_path, full_app_config)
|
||||
|
||||
if updated_content:
|
||||
with open(base_settings_path, "w") as f:
|
||||
f.write(updated_content)
|
||||
@@ -150,7 +138,7 @@ class AppManager:
|
||||
UIFormatter.print_error("Could not find USER_DEFINED_APPS section in base.py")
|
||||
return False
|
||||
|
||||
updated_content = insert_apps_into_user_defined_apps(content, [full_app_config])
|
||||
updated_content = CommonUtils.insert_apps_into_user_defined_apps(content, [full_app_config])
|
||||
if not updated_content:
|
||||
return False
|
||||
|
||||
@@ -177,12 +165,12 @@ class AppManager:
|
||||
self._project_structure_cache = (nested, nested_dir, apps_base_dir)
|
||||
else:
|
||||
# Fallback to old detection method
|
||||
project_dir, settings_base_path = find_project_dir(self.current_dir)
|
||||
project_dir, settings_base_path = CommonUtils.find_project_dir(self.current_dir)
|
||||
|
||||
if project_dir is None:
|
||||
self._project_structure_cache = (False, None, self.current_dir)
|
||||
else:
|
||||
self._project_structure_cache = detect_nested_structure_from_settings(
|
||||
self._project_structure_cache = CommonUtils.detect_nested_structure_from_settings(
|
||||
settings_base_path, self.current_dir
|
||||
)
|
||||
return self._project_structure_cache
|
||||
@@ -210,7 +198,7 @@ class AppManager:
|
||||
return False
|
||||
|
||||
# Check for Single Folder structure (models/ and api/ exist in project dir)
|
||||
project_dir, _ = find_project_dir(self.current_dir)
|
||||
project_dir, _ = CommonUtils.find_project_dir(self.current_dir)
|
||||
if project_dir:
|
||||
has_models = os.path.isdir(os.path.join(project_dir, "models"))
|
||||
has_api = os.path.isdir(os.path.join(project_dir, "api"))
|
||||
@@ -223,11 +211,11 @@ class AppManager:
|
||||
"""Create an app following the predefined nested structure with custom templates."""
|
||||
app_dir = os.path.join(apps_dir, self.app_name)
|
||||
os.makedirs(app_dir, exist_ok=True)
|
||||
create_init_file(app_dir, f"Created apps/{self.app_name}/__init__.py")
|
||||
CommonUtils.create_init_file(app_dir, f"Created apps/{self.app_name}/__init__.py")
|
||||
|
||||
model_class_name = "".join([part.capitalize() for part in self.app_name.split("_")])
|
||||
app_module = f"apps.{self.app_name}"
|
||||
app_config_name = self.app_name.title().replace('_', '')
|
||||
app_config_name = self.app_name.title().replace("_", "")
|
||||
base_context = {
|
||||
"app_name": self.app_name,
|
||||
"app_config_name": app_config_name,
|
||||
@@ -235,7 +223,7 @@ class AppManager:
|
||||
"model_class_name": model_class_name,
|
||||
}
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(app_dir, "apps.py"),
|
||||
"components/apps.py-tpl",
|
||||
{"app_name": self.app_name},
|
||||
@@ -251,17 +239,26 @@ class AppManager:
|
||||
}
|
||||
for folder, files in subfolders.items():
|
||||
folder_path = os.path.join(app_dir, folder)
|
||||
create_directory_with_init(folder_path, f"Created apps/{self.app_name}/{folder}/__init__.py")
|
||||
CommonUtils.create_directory_with_init(folder_path, f"Created apps/{self.app_name}/{folder}/__init__.py")
|
||||
for filename, template in files:
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
file_path, template, base_context, f"Created apps/{self.app_name}/{folder}/{filename}"
|
||||
)
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(app_dir, "urls.py"),
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "drf_router", "app_module": app_module, "view_file": f"{self.app_name}_view", "viewset_name": f"{model_class_name}ViewSet", "router_prefix": "", "basename": self.app_name, "has_viewset": True, "has_include": False},
|
||||
{
|
||||
"url_type": "drf_router",
|
||||
"app_module": app_module,
|
||||
"view_file": f"{self.app_name}_view",
|
||||
"viewset_name": f"{model_class_name}ViewSet",
|
||||
"router_prefix": "",
|
||||
"basename": self.app_name,
|
||||
"has_viewset": True,
|
||||
"has_include": False,
|
||||
},
|
||||
f"Created apps/{self.app_name}/urls.py",
|
||||
)
|
||||
|
||||
|
||||
+64
-45
@@ -5,20 +5,14 @@ Handles creation and modification of project files.
|
||||
|
||||
import os
|
||||
|
||||
from djinit.core.base import BaseService
|
||||
from djinit.services.templates import template_engine
|
||||
from djinit.utils.common import (
|
||||
calculate_app_module_paths,
|
||||
change_cwd,
|
||||
create_directory_with_init,
|
||||
create_file_from_template,
|
||||
create_file_with_content,
|
||||
get_package_name,
|
||||
)
|
||||
from djinit.utils.common import CommonUtils
|
||||
|
||||
|
||||
class FileManager:
|
||||
class FileManager(BaseService):
|
||||
def __init__(self, project_root: str, project_name: str, app_names: list, metadata: dict):
|
||||
self.project_root = project_root
|
||||
super().__init__(project_root=project_root)
|
||||
self.project_name = project_name
|
||||
self.app_names = app_names
|
||||
self.metadata = metadata
|
||||
@@ -38,9 +32,9 @@ class FileManager:
|
||||
) -> None:
|
||||
"""Helper to render template and create file."""
|
||||
target_dir = base_dir or self.project_root
|
||||
with change_cwd(target_dir):
|
||||
with CommonUtils.change_cwd(target_dir):
|
||||
content = template_engine.render_template(template_path, context)
|
||||
create_file_with_content(filepath, content, message, should_format=should_format)
|
||||
CommonUtils.create_file_with_content(filepath, content, message, should_format=should_format)
|
||||
|
||||
def _create_files_from_specs(self, base_dir: str, folder_specs: dict, base_context: dict = None) -> None:
|
||||
"""Helper to create multiple folders and files from a specification dict."""
|
||||
@@ -48,25 +42,27 @@ class FileManager:
|
||||
|
||||
for folder_name, file_templates in folder_specs.items():
|
||||
folder_path = os.path.join(base_dir, folder_name)
|
||||
create_directory_with_init(folder_path, f"Created {folder_path}/__init__.py")
|
||||
CommonUtils.create_directory_with_init(folder_path, f"Created {folder_path}/__init__.py")
|
||||
|
||||
for filename, template_path in file_templates:
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
create_file_from_template(file_path, template_path, base_context, f"Created {file_path}")
|
||||
CommonUtils.create_file_from_template(file_path, template_path, base_context, f"Created {file_path}")
|
||||
|
||||
def _create_apps_py(self, app_dir: str, app_name: str, message: str = None, should_format: bool = False) -> None:
|
||||
"""Helper to create apps.py file for an app."""
|
||||
apps_py_path = os.path.join(app_dir, "apps.py")
|
||||
context = {"app_name": app_name} if app_name else {}
|
||||
msg = message or f"Created {os.path.basename(app_dir)}/apps.py"
|
||||
create_file_from_template(apps_py_path, "components/apps.py-tpl", context, msg, should_format=should_format)
|
||||
CommonUtils.create_file_from_template(
|
||||
apps_py_path, "components/apps.py-tpl", context, msg, should_format=should_format
|
||||
)
|
||||
|
||||
def _create_subdirectories_with_init(self, base_dir: str, subdirs: list, prefix: str = "") -> None:
|
||||
"""Helper to create multiple subdirectories with __init__.py files."""
|
||||
for subdir in subdirs:
|
||||
subdir_path = os.path.join(base_dir, subdir)
|
||||
message = f"Created {prefix}{subdir}/__init__.py" if prefix else f"Created {subdir}/__init__.py"
|
||||
create_directory_with_init(subdir_path, message)
|
||||
CommonUtils.create_directory_with_init(subdir_path, message)
|
||||
|
||||
def _create_settings_package(self, settings_dir: str, base_context: dict, prefix: str) -> None:
|
||||
"""Helper to create settings package files (base, development, production)."""
|
||||
@@ -81,8 +77,8 @@ class FileManager:
|
||||
transformed_apps = []
|
||||
for app in base_settings_context["app_names"]:
|
||||
# Transform to full AppConfig path: module.path -> module.path.apps.ConfigName
|
||||
short_name = app.split('.')[-1]
|
||||
config_name = short_name.title().replace('_', '') + 'Config'
|
||||
short_name = app.split(".")[-1]
|
||||
config_name = short_name.title().replace("_", "") + "Config"
|
||||
# If it's the special "apps" container in unified structure, we might want to skip or handle differently
|
||||
# But assumes all user apps follow standard djinit structure
|
||||
full_path = f"{app}.apps.{config_name}"
|
||||
@@ -169,7 +165,7 @@ class FileManager:
|
||||
|
||||
def create_pyproject(self, metadata: dict) -> None:
|
||||
package_name = metadata.get("package_name", "backend")
|
||||
package_name = get_package_name(package_name)
|
||||
package_name = CommonUtils.get_package_name(package_name)
|
||||
context = {
|
||||
"package_name": package_name,
|
||||
"project_name": self.project_name,
|
||||
@@ -184,7 +180,7 @@ class FileManager:
|
||||
|
||||
def create_project_urls(self) -> None:
|
||||
"""Create project urls.py using project_config/urls.py-tpl template (replaces update_project_urls)."""
|
||||
effective_app_modules = calculate_app_module_paths(self.app_names, self.metadata)
|
||||
effective_app_modules = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
|
||||
context = {
|
||||
"url_type": "project",
|
||||
"project_name": self.project_name,
|
||||
@@ -212,10 +208,10 @@ class FileManager:
|
||||
|
||||
def create_predefined_structure(self) -> None:
|
||||
apps_dir = os.path.join(self.project_root, "apps")
|
||||
create_directory_with_init(apps_dir, "Created apps/__init__.py")
|
||||
CommonUtils.create_directory_with_init(apps_dir, "Created apps/__init__.py")
|
||||
|
||||
users_dir = os.path.join(apps_dir, "users")
|
||||
create_directory_with_init(users_dir, "Created apps/users/__init__.py")
|
||||
CommonUtils.create_directory_with_init(users_dir, "Created apps/users/__init__.py")
|
||||
self._create_apps_py(users_dir, "users")
|
||||
|
||||
users_subfolders = {
|
||||
@@ -228,15 +224,26 @@ class FileManager:
|
||||
self._create_files_from_specs(users_dir, users_subfolders, {"app_module": "apps.users"})
|
||||
|
||||
users_urls_path = os.path.join(users_dir, "urls.py")
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
users_urls_path,
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "drf_router", "app_module": "apps.users", "view_file": "user_view", "viewset_name": "UserViewSet", "router_prefix": "users", "basename": "users", "has_viewset": True, "has_app_name": True, "app_name": "users", "has_include": True},
|
||||
{
|
||||
"url_type": "drf_router",
|
||||
"app_module": "apps.users",
|
||||
"view_file": "user_view",
|
||||
"viewset_name": "UserViewSet",
|
||||
"router_prefix": "users",
|
||||
"basename": "users",
|
||||
"has_viewset": True,
|
||||
"has_app_name": True,
|
||||
"app_name": "users",
|
||||
"has_include": True,
|
||||
},
|
||||
"Created apps/users/urls.py",
|
||||
)
|
||||
|
||||
core_dir = os.path.join(apps_dir, "core")
|
||||
create_directory_with_init(core_dir, "Created apps/core/__init__.py")
|
||||
CommonUtils.create_directory_with_init(core_dir, "Created apps/core/__init__.py")
|
||||
|
||||
core_subfolders = {
|
||||
"utils": [("responses.py", "presets/predefined/core/utils/responses.py-tpl")],
|
||||
@@ -246,22 +253,32 @@ class FileManager:
|
||||
self._create_files_from_specs(core_dir, core_subfolders, {})
|
||||
|
||||
exceptions_path = os.path.join(core_dir, "exceptions.py")
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
exceptions_path, "presets/predefined/core/exceptions.py-tpl", {}, "Created apps/core/exceptions.py"
|
||||
)
|
||||
|
||||
api_dir = os.path.join(self.project_root, "api")
|
||||
create_directory_with_init(api_dir, "Created api/__init__.py")
|
||||
CommonUtils.create_directory_with_init(api_dir, "Created api/__init__.py")
|
||||
api_urls_path = os.path.join(api_dir, "urls.py")
|
||||
create_file_from_template(api_urls_path, "config/urls.py-tpl", {"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "api"}, "Created api/urls.py")
|
||||
CommonUtils.create_file_from_template(
|
||||
api_urls_path,
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "api"},
|
||||
"Created api/urls.py",
|
||||
)
|
||||
|
||||
api_v1_dir = os.path.join(api_dir, "v1")
|
||||
create_directory_with_init(api_v1_dir, "Created api/v1/__init__.py")
|
||||
CommonUtils.create_directory_with_init(api_v1_dir, "Created api/v1/__init__.py")
|
||||
api_v1_urls_path = os.path.join(api_v1_dir, "urls.py")
|
||||
create_file_from_template(api_v1_urls_path, "config/urls.py-tpl", {"url_type": "api_version", "app_name": "v1", "app_list": ["users"], "app_module": "apps"}, "Created api/v1/urls.py")
|
||||
CommonUtils.create_file_from_template(
|
||||
api_v1_urls_path,
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "api_version", "app_name": "v1", "app_list": ["users"], "app_module": "apps"},
|
||||
"Created api/v1/urls.py",
|
||||
)
|
||||
|
||||
project_urls_path = os.path.join(self.project_configs, "urls.py")
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
project_urls_path,
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "project", "api_module": "api", "include_api": True},
|
||||
@@ -272,10 +289,10 @@ class FileManager:
|
||||
def create_unified_structure(self) -> None:
|
||||
# 1. Create 'core' directory (Project Config)
|
||||
core_dir = os.path.join(self.project_root, "core")
|
||||
create_directory_with_init(core_dir, "Created core/__init__.py")
|
||||
CommonUtils.create_directory_with_init(core_dir, "Created core/__init__.py")
|
||||
|
||||
settings_dir = os.path.join(core_dir, "settings")
|
||||
create_directory_with_init(settings_dir, "Created core/settings/__init__.py")
|
||||
CommonUtils.create_directory_with_init(settings_dir, "Created core/settings/__init__.py")
|
||||
|
||||
# Create settings files
|
||||
base_context = {
|
||||
@@ -291,7 +308,7 @@ class FileManager:
|
||||
|
||||
# 2. Create 'apps' directory (The Main App)
|
||||
apps_dir = os.path.join(self.project_root, "apps")
|
||||
create_directory_with_init(apps_dir, "Created apps/__init__.py")
|
||||
CommonUtils.create_directory_with_init(apps_dir, "Created apps/__init__.py")
|
||||
|
||||
# Create apps.py for the 'apps' app
|
||||
self._create_apps_py(apps_dir, "apps", "Created apps/apps.py", should_format=True)
|
||||
@@ -302,9 +319,9 @@ class FileManager:
|
||||
|
||||
# Create 'api' directory with v1
|
||||
api_dir = os.path.join(apps_dir, "api")
|
||||
create_directory_with_init(api_dir, "Created apps/api/__init__.py")
|
||||
CommonUtils.create_directory_with_init(api_dir, "Created apps/api/__init__.py")
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(api_dir, "urls.py"),
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "apps.api"},
|
||||
@@ -313,9 +330,9 @@ class FileManager:
|
||||
)
|
||||
|
||||
api_v1_dir = os.path.join(api_dir, "v1")
|
||||
create_directory_with_init(api_v1_dir, "Created apps/api/v1/__init__.py")
|
||||
CommonUtils.create_directory_with_init(api_v1_dir, "Created apps/api/v1/__init__.py")
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(api_v1_dir, "urls.py"),
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "api_version", "app_name": "v1", "app_list": [], "app_module": "apps"},
|
||||
@@ -325,10 +342,10 @@ class FileManager:
|
||||
|
||||
def create_single_structure(self) -> None:
|
||||
project_dir = os.path.join(self.project_root, self.module_name)
|
||||
create_directory_with_init(project_dir, f"Created {self.module_name}/__init__.py")
|
||||
CommonUtils.create_directory_with_init(project_dir, f"Created {self.module_name}/__init__.py")
|
||||
|
||||
settings_dir = os.path.join(project_dir, "settings")
|
||||
create_directory_with_init(settings_dir, f"Created {self.module_name}/settings/__init__.py")
|
||||
CommonUtils.create_directory_with_init(settings_dir, f"Created {self.module_name}/settings/__init__.py")
|
||||
|
||||
# Create settings files
|
||||
base_context = {
|
||||
@@ -347,28 +364,28 @@ class FileManager:
|
||||
components = ["admin", "api", "models", "tests"]
|
||||
self._create_subdirectories_with_init(project_dir, components, f"{self.module_name}/")
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(project_dir, "admin", "__init__.py"),
|
||||
"components/admin.py-tpl",
|
||||
{},
|
||||
f"Created {self.module_name}/admin/__init__.py",
|
||||
)
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(project_dir, "models", "__init__.py"),
|
||||
"components/models.py-tpl",
|
||||
{},
|
||||
f"Created {self.module_name}/models/__init__.py",
|
||||
)
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(project_dir, "api", "README.md"),
|
||||
"components/api_readme.md-tpl",
|
||||
{"module_name": self.module_name},
|
||||
f"Created {self.module_name}/api/README.md",
|
||||
)
|
||||
|
||||
create_file_from_template(
|
||||
CommonUtils.create_file_from_template(
|
||||
os.path.join(project_dir, "models", "README.md"),
|
||||
"components/models_readme.md-tpl",
|
||||
{"module_name": self.module_name},
|
||||
@@ -447,4 +464,6 @@ class FileManager:
|
||||
}
|
||||
|
||||
filepath = os.path.join(self.project_root, ".djinit")
|
||||
create_file_with_content(filepath, json.dumps(config, indent=4), "Created .djinit configuration file")
|
||||
CommonUtils.create_file_with_content(
|
||||
filepath, json.dumps(config, indent=4), "Created .djinit configuration file"
|
||||
)
|
||||
+28
-30
@@ -5,25 +5,22 @@ Handles creation of Django projects and apps.
|
||||
|
||||
import os
|
||||
|
||||
from djinit.core.base import BaseService
|
||||
from djinit.ui.console import UIFormatter
|
||||
from djinit.utils.common 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,
|
||||
)
|
||||
from djinit.utils.common import CommonUtils
|
||||
from djinit.utils.django import DjangoHelper
|
||||
|
||||
|
||||
class ProjectManager:
|
||||
class ProjectManager(BaseService):
|
||||
def __init__(self, project_dir: str, project_name: str, app_names: list, metadata: dict):
|
||||
super().__init__(project_root=os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir))
|
||||
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)
|
||||
# self.project_root is set by BaseService via super().__init__ but we need to ensure it matches logic
|
||||
# logic was: self.project_root = os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir)
|
||||
# which I passed to super. So I can remove the line setting self.project_root here.
|
||||
self.module_name = metadata.get("project_module_name") or self.project_name
|
||||
|
||||
def create_project(self) -> None:
|
||||
@@ -44,10 +41,12 @@ class ProjectManager:
|
||||
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")
|
||||
CommonUtils.create_directory_with_init(
|
||||
apps_base_dir, f"Created {os.path.basename(apps_base_dir)}/__init__.py"
|
||||
)
|
||||
|
||||
# Calculate module paths for correct AppConfig generation (e.g. src.users)
|
||||
app_module_paths = calculate_app_module_paths(self.app_names, self.metadata)
|
||||
app_module_paths = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
|
||||
|
||||
for i, app_name in enumerate(self.app_names):
|
||||
app_module = app_module_paths[i]
|
||||
@@ -58,30 +57,30 @@ class ProjectManager:
|
||||
|
||||
def add_apps_to_settings(self) -> None:
|
||||
"""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)
|
||||
base_settings_path = CommonUtils.get_base_settings_path(self.project_root, self.module_name)
|
||||
|
||||
if not os.path.exists(base_settings_path):
|
||||
from djinit.utils.exceptions import ConfigError
|
||||
|
||||
raise ConfigError("Could not find base.py settings file")
|
||||
|
||||
content = read_base_settings(self.project_root, self.module_name)
|
||||
content = CommonUtils.read_base_settings(self.project_root, self.module_name)
|
||||
if content is None:
|
||||
from djinit.utils.exceptions import ConfigError
|
||||
|
||||
raise ConfigError("Could not read base.py settings file")
|
||||
|
||||
app_module_paths = calculate_app_module_paths(self.app_names, self.metadata)
|
||||
app_module_paths = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
|
||||
|
||||
# Transform to full AppConfig paths (e.g. apps.users.apps.UsersConfig)
|
||||
full_config_paths = []
|
||||
for app in app_module_paths:
|
||||
short_name = app.split('.')[-1]
|
||||
config_name = short_name.title().replace('_', '') + 'Config'
|
||||
short_name = app.split(".")[-1]
|
||||
config_name = short_name.title().replace("_", "") + "Config"
|
||||
full_config_paths.append(f"{app}.apps.{config_name}")
|
||||
|
||||
existing_apps = extract_existing_apps(content)
|
||||
|
||||
existing_apps = CommonUtils.extract_existing_apps(content)
|
||||
|
||||
# Check against both module path and full config path to avoid duplicates
|
||||
apps_to_add = []
|
||||
for i, full_path in enumerate(full_config_paths):
|
||||
@@ -89,11 +88,11 @@ class ProjectManager:
|
||||
if full_path not in existing_apps and mod_path not in existing_apps:
|
||||
apps_to_add.append(full_path)
|
||||
elif mod_path in existing_apps:
|
||||
# If legacy module path exists, we could upgrade it here,
|
||||
# If legacy module path exists, we could upgrade it here,
|
||||
# but ProjectManager is typically initialized on creation.
|
||||
# For now, let's just not add a duplicate.
|
||||
# Actually, let's upgrade it if we can
|
||||
pass
|
||||
# Actually, let's upgrade it if we can
|
||||
pass
|
||||
|
||||
# If we found apps to add, insert them
|
||||
if not apps_to_add:
|
||||
@@ -102,20 +101,19 @@ class ProjectManager:
|
||||
for i, full_path in enumerate(full_config_paths):
|
||||
mod_path = app_module_paths[i]
|
||||
if mod_path in existing_apps and full_path not in existing_apps:
|
||||
from djinit.utils.common import replace_app_in_user_defined_apps
|
||||
content = replace_app_in_user_defined_apps(content, mod_path, full_path)
|
||||
upgraded = True
|
||||
content = CommonUtils.replace_app_in_user_defined_apps(content, mod_path, full_path)
|
||||
upgraded = True
|
||||
|
||||
if upgraded:
|
||||
with open(base_settings_path, "w") as f:
|
||||
with open(base_settings_path, "w") as f:
|
||||
f.write(content)
|
||||
UIFormatter.print_success("Upgraded existing apps to full AppConfig paths")
|
||||
return
|
||||
|
||||
UIFormatter.print_success("Upgraded existing apps to full AppConfig paths")
|
||||
return
|
||||
|
||||
UIFormatter.print_success("All apps already configured in USER_DEFINED_APPS")
|
||||
return
|
||||
|
||||
updated_content = insert_apps_into_user_defined_apps(content, apps_to_add)
|
||||
updated_content = CommonUtils.insert_apps_into_user_defined_apps(content, apps_to_add)
|
||||
if not updated_content:
|
||||
from djinit.utils.exceptions import ConfigError
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from djinit.core.base import BaseService
|
||||
from djinit.core.parser import InFileLogicParser
|
||||
|
||||
|
||||
class TemplateEngine:
|
||||
class TemplateEngine(BaseService):
|
||||
"""Template engine for rendering file templates."""
|
||||
|
||||
def __init__(self, template_dir: str = None):
|
||||
@@ -23,6 +24,8 @@ class TemplateEngine:
|
||||
# djinit/services/templates.py -> djinit/services -> djinit -> templates
|
||||
template_dir = Path(__file__).resolve().parent.parent / "templates"
|
||||
|
||||
super().__init__()
|
||||
|
||||
if not template_dir.exists():
|
||||
raise FileNotFoundError(f"Template directory not found: {template_dir}")
|
||||
|
||||
@@ -43,7 +46,7 @@ class TemplateEngine:
|
||||
if not os.path.exists(template_path):
|
||||
raise FileNotFoundError(f"Template not found: {template_path}")
|
||||
|
||||
with open(template_path, "r") as f:
|
||||
with open(template_path) as f:
|
||||
template_text = f.read()
|
||||
|
||||
return self.parser.render(template_text, context)
|
||||
|
||||
Reference in new issue
Block a user