ntc-dev-kit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ntc/__init__.py +5 -0
- ntc/app_creator.py +296 -0
- ntc/cli.py +4 -0
- ntc/cli_enhanced.py +820 -0
- ntc/composer/__init__.py +0 -0
- ntc/composer/main.py +443 -0
- ntc/deployment/__init__.py +0 -0
- ntc/templates/__init__.py +2 -0
- ntc/templates/auth_app/__init__.py +0 -0
- ntc/templates/auth_app/admin.py +24 -0
- ntc/templates/auth_app/apps.py +6 -0
- ntc/templates/auth_app/models.py +30 -0
- ntc/templates/auth_app/serializers.py +101 -0
- ntc/templates/auth_app/urls.py +19 -0
- ntc/templates/auth_app/views.py +175 -0
- ntc/templates/drf_structure/__init__.py +1 -0
- ntc/templates/drf_structure/drf_structure.md +83 -0
- ntc/templates/drf_structure/main.py +389 -0
- ntc/templates/drf_structure/structure.json +126 -0
- ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +37 -0
- ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +30 -0
- ntc/templates/drf_structure/templates/CLAUDE.md +194 -0
- ntc/templates/drf_structure/templates/README.md +117 -0
- ntc/templates/drf_structure/templates/apps/app_admin.py +3 -0
- ntc/templates/drf_structure/templates/apps/app_apps.py +6 -0
- ntc/templates/drf_structure/templates/apps/app_models.py +4 -0
- ntc/templates/drf_structure/templates/apps/app_urls.py +8 -0
- ntc/templates/drf_structure/templates/apps/app_views.py +6 -0
- ntc/templates/drf_structure/templates/apps/common_apps.py +6 -0
- ntc/templates/drf_structure/templates/apps/common_models.py +8 -0
- ntc/templates/drf_structure/templates/config/gunicorn_config.py +9 -0
- ntc/templates/drf_structure/templates/config/http.conf +33 -0
- ntc/templates/drf_structure/templates/core/base_model.py +9 -0
- ntc/templates/drf_structure/templates/core/formatter.py +5 -0
- ntc/templates/drf_structure/templates/core/logger.py +8 -0
- ntc/templates/drf_structure/templates/core/pagination.py +6 -0
- ntc/templates/drf_structure/templates/core/response_wrapper.py +7 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +108 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/client.py +296 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +102 -0
- ntc/templates/drf_structure/templates/core/services/object_storage.py +285 -0
- ntc/templates/drf_structure/templates/django_project/asgi.py +5 -0
- ntc/templates/drf_structure/templates/django_project/celery.py +8 -0
- ntc/templates/drf_structure/templates/django_project/dev_settings.py +2 -0
- ntc/templates/drf_structure/templates/django_project/django_settings.txt +322 -0
- ntc/templates/drf_structure/templates/django_project/management_command.py +114 -0
- ntc/templates/drf_structure/templates/django_project/prod_settings.py +3 -0
- ntc/templates/drf_structure/templates/django_project/schema.py +7 -0
- ntc/templates/drf_structure/templates/django_project/urls.py +6 -0
- ntc/templates/drf_structure/templates/django_project/wsgi.py +5 -0
- ntc/templates/drf_structure/templates/docker/docker-compose.yml +11 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +201 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +722 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +151 -0
- ntc/templates/drf_structure/templates/root/README.md +307 -0
- ntc/templates/drf_structure/templates/root/django_env.txt +98 -0
- ntc/templates/drf_structure/templates/root/django_requirements.txt +24 -0
- ntc/templates/drf_structure/templates/root/gitignore.txt +6 -0
- ntc/templates/drf_structure/templates/root/manage.py +11 -0
- ntc/templates/flutter/.gitignore +44 -0
- ntc/templates/flutter/.knowledge/knowledge_description.md +215 -0
- ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +310 -0
- ntc/templates/flutter/.knowledge/knowledge_structure.md +83 -0
- ntc/templates/flutter/CLAUDE.md +190 -0
- ntc/templates/flutter/__init__.py +0 -0
- ntc/templates/flutter/analysis_options.yaml +9 -0
- ntc/templates/flutter/main.py +187 -0
- ntc/templates/flutter/pubspec.yaml +65 -0
- ntc/templates/flutter/structure.json +51 -0
- ntc/templates/nextjs/.env.example +10 -0
- ntc/templates/nextjs/.gitignore +36 -0
- ntc/templates/nextjs/CLAUDE.md +181 -0
- ntc/templates/nextjs/README.md +94 -0
- ntc/templates/nextjs/__init__.py +0 -0
- ntc/templates/nextjs/knowledge/knowledge_description.md +208 -0
- ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +460 -0
- ntc/templates/nextjs/knowledge/knowledge_structure.md +75 -0
- ntc/templates/nextjs/main.py +224 -0
- ntc/templates/nextjs/package.json +25 -0
- ntc/templates/nextjs/src/app/globals.css +26 -0
- ntc/templates/nextjs/src/app/layout.tsx +59 -0
- ntc/templates/nextjs/src/app/page.tsx +21 -0
- ntc/templates/nextjs/src/components/Header.tsx +16 -0
- ntc/templates/nextjs/src/services/api.ts +81 -0
- ntc/templates/nextjs/src/utils/constants.ts +1 -0
- ntc/templates/nextjs/structure.json +32 -0
- ntc/templates/nextjs/tsconfig.json +27 -0
- ntc/templates/react_admin/.env.example +1 -0
- ntc/templates/react_admin/.gitignore +10 -0
- ntc/templates/react_admin/CLAUDE.md +210 -0
- ntc/templates/react_admin/README.md +72 -0
- ntc/templates/react_admin/__init__.py +0 -0
- ntc/templates/react_admin/index.html +13 -0
- ntc/templates/react_admin/knowledge/knowledge_description.md +202 -0
- ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +448 -0
- ntc/templates/react_admin/knowledge/knowledge_structure.md +97 -0
- ntc/templates/react_admin/main.py +208 -0
- ntc/templates/react_admin/package.json +35 -0
- ntc/templates/react_admin/src/App.tsx +60 -0
- ntc/templates/react_admin/src/components/ProtectedRoute.tsx +16 -0
- ntc/templates/react_admin/src/components/Sidebar.tsx +90 -0
- ntc/templates/react_admin/src/components/TopNav.tsx +44 -0
- ntc/templates/react_admin/src/context/AuthContext.tsx +107 -0
- ntc/templates/react_admin/src/context/ThemeContext.tsx +34 -0
- ntc/templates/react_admin/src/context/WorkspaceContext.tsx +28 -0
- ntc/templates/react_admin/src/hooks/useApi.ts +39 -0
- ntc/templates/react_admin/src/index.css +34 -0
- ntc/templates/react_admin/src/layouts/AuthLayout.tsx +23 -0
- ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +24 -0
- ntc/templates/react_admin/src/main.tsx +5 -0
- ntc/templates/react_admin/src/pages/DashboardPage.tsx +10 -0
- ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +79 -0
- ntc/templates/react_admin/src/pages/auth/RegisterPage.tsx +93 -0
- ntc/templates/react_admin/src/services/api.service.ts +44 -0
- ntc/templates/react_admin/src/services/base.service.ts +193 -0
- ntc/templates/react_admin/structure.json +52 -0
- ntc/templates/react_admin/tsconfig.app.json +25 -0
- ntc/templates/react_admin/tsconfig.json +7 -0
- ntc/templates/react_admin/tsconfig.node.json +19 -0
- ntc/templates/react_admin/vite.config.ts +13 -0
- ntc/templates/react_structure/CLAUDE.md +176 -0
- ntc/templates/react_structure/__init__.py +1 -0
- ntc/templates/react_structure/index.css +21 -0
- ntc/templates/react_structure/index.html +13 -0
- ntc/templates/react_structure/knowledge/knowledge_description.md +170 -0
- ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +411 -0
- ntc/templates/react_structure/knowledge/knowledge_structure.md +74 -0
- ntc/templates/react_structure/main.py +217 -0
- ntc/templates/react_structure/package.json +32 -0
- ntc/templates/react_structure/src/App.tsx +63 -0
- ntc/templates/react_structure/src/components/Header.tsx +51 -0
- ntc/templates/react_structure/src/main.tsx +5 -0
- ntc/templates/react_structure/src/services/api.ts +90 -0
- ntc/templates/react_structure/src/utils/constants.ts +1 -0
- ntc/templates/react_structure/structure.json +65 -0
- ntc/templates/react_structure/tsconfig.app.json +25 -0
- ntc/templates/react_structure/tsconfig.json +7 -0
- ntc/templates/react_structure/tsconfig.node.json +19 -0
- ntc/templates/react_structure/vite.config.ts +12 -0
- ntc/utils/__init__.py +1 -0
- ntc/utils/environment.py +67 -0
- ntc/utils/output.py +41 -0
- ntc_dev_kit-1.0.0.dist-info/METADATA +188 -0
- ntc_dev_kit-1.0.0.dist-info/RECORD +148 -0
- ntc_dev_kit-1.0.0.dist-info/WHEEL +5 -0
- ntc_dev_kit-1.0.0.dist-info/entry_points.txt +2 -0
- ntc_dev_kit-1.0.0.dist-info/licenses/LICENSE +21 -0
- ntc_dev_kit-1.0.0.dist-info/top_level.txt +1 -0
ntc/cli_enhanced.py
ADDED
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import msvcrt
|
|
5
|
+
import time
|
|
6
|
+
from colorama import Fore, Back, Style, init
|
|
7
|
+
from ntc.templates.drf_structure.main import init_drf_project, init_drf_project_in_current_dir
|
|
8
|
+
from ntc.templates.react_structure.main import init_react_project, init_react_project_in_current_dir
|
|
9
|
+
from ntc.templates.react_admin.main import init_react_admin_project, init_react_admin_project_in_current_dir
|
|
10
|
+
from ntc.templates.nextjs.main import init_nextjs_project, init_nextjs_project_in_current_dir
|
|
11
|
+
from ntc.templates.flutter.main import init_flutter_project, init_flutter_project_in_current_dir
|
|
12
|
+
from ntc.composer.main import compose_project, compose_project_in_current_dir, start_services
|
|
13
|
+
from ntc.app_creator import create_app_structure
|
|
14
|
+
|
|
15
|
+
# Initialize colorama for cross-platform colored output
|
|
16
|
+
init(autoreset=True)
|
|
17
|
+
|
|
18
|
+
def print_banner():
|
|
19
|
+
"""Display the clean NTC welcome banner."""
|
|
20
|
+
print(f"""
|
|
21
|
+
{Fore.CYAN}╔{'═' * 75}╗{Style.RESET_ALL}
|
|
22
|
+
{Fore.CYAN}║{' ' * 75}║{Style.RESET_ALL}
|
|
23
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}███╗ ██╗████████╗ ██████╗{' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
24
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}████╗ ██║╚══██╔══╝██╔════╝{' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
25
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}██╔██╗ ██║ ██║ ██║ {' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
26
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}██║╚██╗██║ ██║ ██║ {' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
27
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}██║ ╚████║ ██║ ╚██████╗{' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
28
|
+
{Fore.CYAN}║{' ' * 25}{Fore.YELLOW}{Style.BRIGHT}╚═╝ ╚═══╝ ╚═╝ ╚═════╝{' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
29
|
+
{Fore.CYAN}║{' ' * 75}║{Style.RESET_ALL}
|
|
30
|
+
{Fore.CYAN}║{' ' * 24}{Fore.GREEN}{Style.BRIGHT}🚀 NEXENT TOOLKIT CONSOLE 🚀{' ' * 23}{Fore.CYAN}║{Style.RESET_ALL}
|
|
31
|
+
{Fore.CYAN}║{' ' * 75}║{Style.RESET_ALL}
|
|
32
|
+
{Fore.CYAN}║{' ' * 16}{Fore.WHITE}The ultimate toolkit for API-driven development{' ' * 12}{Fore.CYAN}║{Style.RESET_ALL}
|
|
33
|
+
{Fore.CYAN}║{' ' * 75}║{Style.RESET_ALL}
|
|
34
|
+
{Fore.CYAN}║{' ' * 10}{Fore.GREEN}Backend:{Fore.WHITE} Django/DRF, Laravel {Fore.BLUE}Frontend:{Fore.WHITE} React, Angular, Vue {Fore.CYAN}{' ' * 5}║{Style.RESET_ALL}
|
|
35
|
+
{Fore.CYAN}║{' ' * 75}║{Style.RESET_ALL}
|
|
36
|
+
{Fore.CYAN}╚{'═' * 75}╝{Style.RESET_ALL}""")
|
|
37
|
+
print()
|
|
38
|
+
|
|
39
|
+
def print_welcome_message():
|
|
40
|
+
"""Display a simple welcome message."""
|
|
41
|
+
print(f" {Fore.BLUE}💡 Use arrow keys to navigate • Press Enter to select • Esc to exit{Style.RESET_ALL}")
|
|
42
|
+
print()
|
|
43
|
+
|
|
44
|
+
def print_success(message):
|
|
45
|
+
"""Print success message with green color and checkmark."""
|
|
46
|
+
print(f"{Fore.GREEN}✅ {message}{Style.RESET_ALL}")
|
|
47
|
+
|
|
48
|
+
def print_error(message):
|
|
49
|
+
"""Print error message with red color and X mark."""
|
|
50
|
+
print(f"{Fore.RED}❌ {message}{Style.RESET_ALL}")
|
|
51
|
+
|
|
52
|
+
def print_warning(message):
|
|
53
|
+
"""Print warning message with yellow color and warning sign."""
|
|
54
|
+
print(f"{Fore.YELLOW}⚠️ {message}{Style.RESET_ALL}")
|
|
55
|
+
|
|
56
|
+
def print_info(message):
|
|
57
|
+
"""Print info message with blue color and info icon."""
|
|
58
|
+
print(f"{Fore.CYAN}ℹ️ {message}{Style.RESET_ALL}")
|
|
59
|
+
|
|
60
|
+
def print_question(message):
|
|
61
|
+
"""Print question with bright white color and question mark."""
|
|
62
|
+
return input(f"{Fore.WHITE}{Style.BRIGHT}❓ {message}{Style.RESET_ALL}")
|
|
63
|
+
|
|
64
|
+
def clear_screen():
|
|
65
|
+
"""Clear the terminal screen."""
|
|
66
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
67
|
+
|
|
68
|
+
def print_choice(number, description, selected=False):
|
|
69
|
+
"""Print a numbered choice option with selection highlighting."""
|
|
70
|
+
if selected:
|
|
71
|
+
print(f"{Back.CYAN}{Fore.WHITE}{Style.BRIGHT} ► [{number}] {description} {Style.RESET_ALL}")
|
|
72
|
+
else:
|
|
73
|
+
print(f" {Fore.YELLOW}[{number}]{Style.RESET_ALL} {Fore.WHITE}{description}{Style.RESET_ALL}")
|
|
74
|
+
|
|
75
|
+
def print_menu_header(title, subtitle=""):
|
|
76
|
+
"""Print an attractive menu header."""
|
|
77
|
+
width = 70
|
|
78
|
+
print(f"\n{Fore.CYAN}{'═' * width}{Style.RESET_ALL}")
|
|
79
|
+
print(f"{Fore.CYAN}║{' ' * ((width - len(title) - 2) // 2)}{Fore.YELLOW}{Style.BRIGHT}{title}{Fore.CYAN}{' ' * ((width - len(title) - 2) // 2)}║{Style.RESET_ALL}")
|
|
80
|
+
if subtitle:
|
|
81
|
+
print(f"{Fore.CYAN}║{' ' * ((width - len(subtitle) - 2) // 2)}{Fore.WHITE}{subtitle}{Fore.CYAN}{' ' * ((width - len(subtitle) - 2) // 2)}║{Style.RESET_ALL}")
|
|
82
|
+
print(f"{Fore.CYAN}{'═' * width}{Style.RESET_ALL}")
|
|
83
|
+
|
|
84
|
+
def print_navigation_help():
|
|
85
|
+
"""Print navigation instructions."""
|
|
86
|
+
print(f"\n{Fore.MAGENTA}🎮 Navigation:{Style.RESET_ALL}")
|
|
87
|
+
print(f" {Fore.GREEN}↑/↓{Style.RESET_ALL} Navigate {Fore.GREEN}Enter{Style.RESET_ALL} Select {Fore.GREEN}Esc{Style.RESET_ALL} Exit")
|
|
88
|
+
|
|
89
|
+
def animate_loading(message, duration=1.5):
|
|
90
|
+
"""Show an animated loading message."""
|
|
91
|
+
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
92
|
+
end_time = time.time() + duration
|
|
93
|
+
|
|
94
|
+
while time.time() < end_time:
|
|
95
|
+
for frame in frames:
|
|
96
|
+
if time.time() >= end_time:
|
|
97
|
+
break
|
|
98
|
+
print(f"\r{Fore.CYAN}{frame} {message}{Style.RESET_ALL}", end="", flush=True)
|
|
99
|
+
time.sleep(0.1)
|
|
100
|
+
|
|
101
|
+
print(f"\r{Fore.GREEN}✅ {message} Complete!{Style.RESET_ALL}")
|
|
102
|
+
time.sleep(0.5)
|
|
103
|
+
|
|
104
|
+
def animate_progress(message, steps=None, duration=3.0):
|
|
105
|
+
"""Show progress animation with percentage on the same line."""
|
|
106
|
+
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
107
|
+
|
|
108
|
+
if steps:
|
|
109
|
+
# Step-based progress
|
|
110
|
+
for i, step in enumerate(steps, 1):
|
|
111
|
+
progress = int((i / len(steps)) * 100)
|
|
112
|
+
for j in range(10): # Show animation for each step
|
|
113
|
+
frame = frames[j % len(frames)]
|
|
114
|
+
print(f"\r{Fore.CYAN}{frame} {message}: {step} {Fore.YELLOW}[{progress}%]{Style.RESET_ALL}", end="", flush=True)
|
|
115
|
+
time.sleep(0.1)
|
|
116
|
+
print(f"\r{Fore.GREEN}✅ {message} Complete! {Fore.YELLOW}[100%]{Style.RESET_ALL}")
|
|
117
|
+
else:
|
|
118
|
+
# Time-based progress
|
|
119
|
+
start_time = time.time()
|
|
120
|
+
end_time = start_time + duration
|
|
121
|
+
|
|
122
|
+
while time.time() < end_time:
|
|
123
|
+
elapsed = time.time() - start_time
|
|
124
|
+
progress = int((elapsed / duration) * 100)
|
|
125
|
+
frame = frames[int(elapsed * 10) % len(frames)]
|
|
126
|
+
|
|
127
|
+
print(f"\r{Fore.CYAN}{frame} {message} {Fore.YELLOW}[{progress}%]{Style.RESET_ALL}", end="", flush=True)
|
|
128
|
+
time.sleep(0.1)
|
|
129
|
+
|
|
130
|
+
print(f"\r{Fore.GREEN}✅ {message} Complete! {Fore.YELLOW}[100%]{Style.RESET_ALL}")
|
|
131
|
+
|
|
132
|
+
print() # New line after completion
|
|
133
|
+
|
|
134
|
+
def animate_dependency_install(requirements_path=None, pip_command=None, env_type="system"):
|
|
135
|
+
"""Show real dependency installation with progress animation."""
|
|
136
|
+
import subprocess
|
|
137
|
+
import threading
|
|
138
|
+
|
|
139
|
+
if not requirements_path or not os.path.exists(requirements_path):
|
|
140
|
+
# Fallback to simulation if no requirements file
|
|
141
|
+
dependencies = [
|
|
142
|
+
"Resolving dependencies",
|
|
143
|
+
"Downloading packages",
|
|
144
|
+
"Installing Django",
|
|
145
|
+
"Installing DRF",
|
|
146
|
+
"Installing Celery",
|
|
147
|
+
"Installing CORS headers",
|
|
148
|
+
"Configuring environment",
|
|
149
|
+
"Setting up database",
|
|
150
|
+
"Creating migrations",
|
|
151
|
+
"Finalizing setup"
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
|
|
155
|
+
|
|
156
|
+
for i, dep in enumerate(dependencies, 1):
|
|
157
|
+
progress = int((i / len(dependencies)) * 100)
|
|
158
|
+
|
|
159
|
+
# Show animation for each dependency
|
|
160
|
+
for j in range(5): # 5 animation cycles per dependency
|
|
161
|
+
frame = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][j % 10]
|
|
162
|
+
# Clear line and print new content
|
|
163
|
+
print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} {dep}... {Fore.YELLOW}[{progress}%]{Style.RESET_ALL}", end="", flush=True)
|
|
164
|
+
time.sleep(0.2)
|
|
165
|
+
|
|
166
|
+
print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully! {Fore.YELLOW}[100%]{Style.RESET_ALL}")
|
|
167
|
+
print()
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# Real dependency installation with animation
|
|
171
|
+
print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
|
|
172
|
+
|
|
173
|
+
# Determine the pip command to use
|
|
174
|
+
if pip_command is None:
|
|
175
|
+
# Default to system pip
|
|
176
|
+
pip_command = [sys.executable, "-m", "pip", "install", "-r", requirements_path]
|
|
177
|
+
|
|
178
|
+
# Animation state
|
|
179
|
+
animation_running = True
|
|
180
|
+
install_complete = False
|
|
181
|
+
install_success = False
|
|
182
|
+
error_message = ""
|
|
183
|
+
|
|
184
|
+
def run_pip_install():
|
|
185
|
+
nonlocal install_complete, install_success, error_message
|
|
186
|
+
try:
|
|
187
|
+
# Run pip install with suppressed output
|
|
188
|
+
result = subprocess.run(
|
|
189
|
+
pip_command,
|
|
190
|
+
capture_output=True,
|
|
191
|
+
text=True,
|
|
192
|
+
check=True
|
|
193
|
+
)
|
|
194
|
+
install_success = True
|
|
195
|
+
except subprocess.CalledProcessError as e:
|
|
196
|
+
install_success = False
|
|
197
|
+
error_message = str(e)
|
|
198
|
+
finally:
|
|
199
|
+
install_complete = True
|
|
200
|
+
|
|
201
|
+
# Start installation in background thread
|
|
202
|
+
install_thread = threading.Thread(target=run_pip_install)
|
|
203
|
+
install_thread.start()
|
|
204
|
+
|
|
205
|
+
# Show animation while installation runs
|
|
206
|
+
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
207
|
+
frame_index = 0
|
|
208
|
+
|
|
209
|
+
while not install_complete:
|
|
210
|
+
frame = frames[frame_index % len(frames)]
|
|
211
|
+
# Clear line completely before writing new content
|
|
212
|
+
env_msg = f" ({env_type} environment)" if env_type != "system" else ""
|
|
213
|
+
print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} Installing packages{env_msg}...{Style.RESET_ALL}", end="", flush=True)
|
|
214
|
+
time.sleep(0.1)
|
|
215
|
+
frame_index += 1
|
|
216
|
+
|
|
217
|
+
# Wait for thread to complete
|
|
218
|
+
install_thread.join()
|
|
219
|
+
|
|
220
|
+
# Show final result
|
|
221
|
+
if install_success:
|
|
222
|
+
print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully!{Style.RESET_ALL}")
|
|
223
|
+
else:
|
|
224
|
+
print(f"\r{' ' * 80}\r{Fore.RED}❌ Failed to install dependencies: {error_message}{Style.RESET_ALL}")
|
|
225
|
+
print(f"{Fore.YELLOW}💡 You can manually install later with: pip install -r requirements.txt{Style.RESET_ALL}")
|
|
226
|
+
|
|
227
|
+
print()
|
|
228
|
+
|
|
229
|
+
def get_key():
|
|
230
|
+
"""Get a single keypress from user."""
|
|
231
|
+
if os.name == 'nt': # Windows
|
|
232
|
+
key = msvcrt.getch()
|
|
233
|
+
if key == b'\xe0': # Arrow keys on Windows
|
|
234
|
+
key = msvcrt.getch()
|
|
235
|
+
if key == b'H': # Up arrow
|
|
236
|
+
return 'up'
|
|
237
|
+
elif key == b'P': # Down arrow
|
|
238
|
+
return 'down'
|
|
239
|
+
elif key == b'\r': # Enter
|
|
240
|
+
return 'enter'
|
|
241
|
+
elif key == b'\x1b': # Escape
|
|
242
|
+
return 'escape'
|
|
243
|
+
else:
|
|
244
|
+
return key.decode('utf-8', errors='ignore')
|
|
245
|
+
else: # Unix/Linux/Mac
|
|
246
|
+
import termios, tty
|
|
247
|
+
fd = sys.stdin.fileno()
|
|
248
|
+
old_settings = termios.tcgetattr(fd)
|
|
249
|
+
try:
|
|
250
|
+
tty.setraw(sys.stdin.fileno())
|
|
251
|
+
key = sys.stdin.read(1)
|
|
252
|
+
if key == '\x1b': # Escape sequence
|
|
253
|
+
key += sys.stdin.read(2)
|
|
254
|
+
if key == '\x1b[A': # Up arrow
|
|
255
|
+
return 'up'
|
|
256
|
+
elif key == '\x1b[B': # Down arrow
|
|
257
|
+
return 'down'
|
|
258
|
+
else:
|
|
259
|
+
return 'escape'
|
|
260
|
+
elif key == '\r' or key == '\n': # Enter
|
|
261
|
+
return 'enter'
|
|
262
|
+
else:
|
|
263
|
+
return key
|
|
264
|
+
finally:
|
|
265
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
266
|
+
|
|
267
|
+
def interactive_menu(title, options, subtitle=""):
|
|
268
|
+
"""Display an interactive menu with keyboard navigation."""
|
|
269
|
+
selected = 0
|
|
270
|
+
|
|
271
|
+
while True:
|
|
272
|
+
clear_screen()
|
|
273
|
+
print_banner()
|
|
274
|
+
print_menu_header(title, subtitle)
|
|
275
|
+
|
|
276
|
+
# Display options
|
|
277
|
+
for i, option in enumerate(options):
|
|
278
|
+
print_choice(i + 1, option['label'], selected == i)
|
|
279
|
+
|
|
280
|
+
print_navigation_help()
|
|
281
|
+
|
|
282
|
+
# Get user input
|
|
283
|
+
key = get_key()
|
|
284
|
+
|
|
285
|
+
if key == 'up':
|
|
286
|
+
selected = (selected - 1) % len(options)
|
|
287
|
+
elif key == 'down':
|
|
288
|
+
selected = (selected + 1) % len(options)
|
|
289
|
+
elif key == 'enter':
|
|
290
|
+
animate_loading(f"Selecting {options[selected]['label']}")
|
|
291
|
+
return options[selected]['value']
|
|
292
|
+
elif key == 'escape':
|
|
293
|
+
print(f"\n{Fore.YELLOW}👋 Goodbye!{Style.RESET_ALL}")
|
|
294
|
+
sys.exit(0)
|
|
295
|
+
elif key.isdigit():
|
|
296
|
+
# Allow direct number selection
|
|
297
|
+
num = int(key) - 1
|
|
298
|
+
if 0 <= num < len(options):
|
|
299
|
+
selected = num
|
|
300
|
+
|
|
301
|
+
def get_project_type():
|
|
302
|
+
"""Ask user to choose between frontend or backend project."""
|
|
303
|
+
options = [
|
|
304
|
+
{'label': '🔧 Backend (API Server)', 'value': 'backend'},
|
|
305
|
+
{'label': '🎨 Frontend (Client Application)', 'value': 'frontend'}
|
|
306
|
+
]
|
|
307
|
+
return interactive_menu("PROJECT TYPE SELECTION", options, "Choose your project architecture")
|
|
308
|
+
|
|
309
|
+
def get_backend_technology():
|
|
310
|
+
"""Ask user to choose backend technology."""
|
|
311
|
+
options = [
|
|
312
|
+
{'label': '🐍 Django REST Framework (Python)', 'value': 'django'},
|
|
313
|
+
{'label': '🐘 Laravel (PHP) - Coming Soon!', 'value': 'laravel'}
|
|
314
|
+
]
|
|
315
|
+
result = interactive_menu("BACKEND TECHNOLOGY", options, "Select your backend framework")
|
|
316
|
+
|
|
317
|
+
if result == 'laravel':
|
|
318
|
+
print_warning("Laravel support is coming soon! Using Django for now.")
|
|
319
|
+
return 'django'
|
|
320
|
+
return result
|
|
321
|
+
|
|
322
|
+
def get_frontend_technology():
|
|
323
|
+
"""Ask user to choose frontend technology."""
|
|
324
|
+
options = [
|
|
325
|
+
{'label': '🌐 Next.js (TypeScript + SEO Optimized)', 'value': 'nextjs'},
|
|
326
|
+
{'label': '📱 Flutter (Dart + Cross-Platform)', 'value': 'flutter'},
|
|
327
|
+
{'label': '⚛️ React Admin Dashboard (TypeScript + Vite)', 'value': 'react-admin'},
|
|
328
|
+
{'label': '⚛️ React (TypeScript + Vite)', 'value': 'react'},
|
|
329
|
+
{'label': '🅰️ Angular (TypeScript) - Coming Soon!', 'value': 'angular'},
|
|
330
|
+
{'label': '💚 Vue.js (TypeScript) - Coming Soon!', 'value': 'vue'}
|
|
331
|
+
]
|
|
332
|
+
result = interactive_menu("FRONTEND TECHNOLOGY", options, "Select your frontend framework")
|
|
333
|
+
|
|
334
|
+
if result in ['angular', 'vue']:
|
|
335
|
+
print_warning(f"{result.title()} support is coming soon! Using React for now.")
|
|
336
|
+
return 'react'
|
|
337
|
+
return result
|
|
338
|
+
|
|
339
|
+
def get_project_name():
|
|
340
|
+
"""Get project name with enhanced input."""
|
|
341
|
+
clear_screen()
|
|
342
|
+
print_banner()
|
|
343
|
+
print_welcome_message()
|
|
344
|
+
print_menu_header("PROJECT SETUP", "Enter your project details")
|
|
345
|
+
|
|
346
|
+
print(f"\n{Fore.CYAN}📝 Project Name:{Style.RESET_ALL}")
|
|
347
|
+
print(f" {Fore.WHITE}• Use lowercase letters, numbers, and underscores{Style.RESET_ALL}")
|
|
348
|
+
print(f" {Fore.WHITE}• Type '.' to use current directory name{Style.RESET_ALL}")
|
|
349
|
+
|
|
350
|
+
project_name = print_question("Enter your project name: ").strip()
|
|
351
|
+
if not project_name:
|
|
352
|
+
print_error("Project name cannot be empty!")
|
|
353
|
+
time.sleep(2)
|
|
354
|
+
return get_project_name()
|
|
355
|
+
|
|
356
|
+
return project_name
|
|
357
|
+
|
|
358
|
+
def get_project_path(project_name, use_current_dir=False):
|
|
359
|
+
"""Get project path with enhanced input."""
|
|
360
|
+
if use_current_dir:
|
|
361
|
+
return os.path.dirname(os.getcwd())
|
|
362
|
+
|
|
363
|
+
clear_screen()
|
|
364
|
+
print_banner()
|
|
365
|
+
print_menu_header("PROJECT LOCATION", "Choose where to create your project")
|
|
366
|
+
|
|
367
|
+
current_dir = os.getcwd()
|
|
368
|
+
print(f"\n{Fore.CYAN}📁 Current Directory:{Style.RESET_ALL}")
|
|
369
|
+
print(f" {Fore.WHITE}{current_dir}{Style.RESET_ALL}")
|
|
370
|
+
|
|
371
|
+
options = [
|
|
372
|
+
{'label': f'📂 Use current directory ({current_dir})', 'value': 'current'},
|
|
373
|
+
{'label': '📁 Choose custom path', 'value': 'custom'}
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
choice = interactive_menu("PROJECT LOCATION", options, "Where should we create your project?")
|
|
377
|
+
|
|
378
|
+
if choice == 'current':
|
|
379
|
+
return current_dir
|
|
380
|
+
else:
|
|
381
|
+
custom_path = print_question(f"Enter custom path (default: {current_dir}): ").strip()
|
|
382
|
+
return custom_path if custom_path else current_dir
|
|
383
|
+
|
|
384
|
+
def show_project_summary(project_name, project_type, technology, project_path):
|
|
385
|
+
"""Show project configuration summary."""
|
|
386
|
+
clear_screen()
|
|
387
|
+
print_banner()
|
|
388
|
+
print_menu_header("PROJECT SUMMARY", "Review your configuration")
|
|
389
|
+
|
|
390
|
+
print(f"\n{Fore.GREEN}✨ Project Configuration:{Style.RESET_ALL}")
|
|
391
|
+
print(f" {Fore.CYAN}📛 Name:{Style.RESET_ALL} {Fore.WHITE}{project_name}{Style.RESET_ALL}")
|
|
392
|
+
print(f" {Fore.CYAN}🏗️ Type:{Style.RESET_ALL} {Fore.WHITE}{project_type.title()}{Style.RESET_ALL}")
|
|
393
|
+
print(f" {Fore.CYAN}🔧 Technology:{Style.RESET_ALL} {Fore.WHITE}{technology.title()}{Style.RESET_ALL}")
|
|
394
|
+
print(f" {Fore.CYAN}📁 Path:{Style.RESET_ALL} {Fore.WHITE}{project_path}{Style.RESET_ALL}")
|
|
395
|
+
|
|
396
|
+
options = [
|
|
397
|
+
{'label': '🚀 Create Project', 'value': 'create'},
|
|
398
|
+
{'label': '🔄 Start Over', 'value': 'restart'},
|
|
399
|
+
{'label': '❌ Cancel', 'value': 'cancel'}
|
|
400
|
+
]
|
|
401
|
+
|
|
402
|
+
return interactive_menu("CONFIRM CREATION", options, "Ready to create your project?")
|
|
403
|
+
|
|
404
|
+
def interactive_init(preset_project_type=None, preset_technology=None):
|
|
405
|
+
"""Interactive project initialization with enhanced UI."""
|
|
406
|
+
while True:
|
|
407
|
+
# Get project name
|
|
408
|
+
project_name = get_project_name()
|
|
409
|
+
|
|
410
|
+
# Handle '.' as current directory shorthand
|
|
411
|
+
use_current_dir = False
|
|
412
|
+
if project_name == ".":
|
|
413
|
+
current_dir = os.getcwd()
|
|
414
|
+
project_name = os.path.basename(current_dir)
|
|
415
|
+
use_current_dir = True
|
|
416
|
+
animate_loading(f"Using current directory name: '{project_name}'")
|
|
417
|
+
|
|
418
|
+
# Get project type
|
|
419
|
+
if preset_project_type:
|
|
420
|
+
project_type = preset_project_type
|
|
421
|
+
else:
|
|
422
|
+
project_type = get_project_type()
|
|
423
|
+
|
|
424
|
+
# Get technology based on project type
|
|
425
|
+
if preset_technology:
|
|
426
|
+
technology = preset_technology
|
|
427
|
+
else:
|
|
428
|
+
if project_type == "backend":
|
|
429
|
+
technology = get_backend_technology()
|
|
430
|
+
else:
|
|
431
|
+
technology = get_frontend_technology()
|
|
432
|
+
|
|
433
|
+
# Get project path
|
|
434
|
+
project_path = get_project_path(project_name, use_current_dir)
|
|
435
|
+
|
|
436
|
+
# Show summary and get confirmation
|
|
437
|
+
action = show_project_summary(project_name, project_type, technology, project_path)
|
|
438
|
+
|
|
439
|
+
if action == 'create':
|
|
440
|
+
break
|
|
441
|
+
elif action == 'restart':
|
|
442
|
+
continue
|
|
443
|
+
else: # cancel
|
|
444
|
+
print(f"\n{Fore.YELLOW}👋 Project creation cancelled. Goodbye!{Style.RESET_ALL}")
|
|
445
|
+
sys.exit(0)
|
|
446
|
+
|
|
447
|
+
# Create the project with enhanced feedback
|
|
448
|
+
animate_loading("Initializing project structure")
|
|
449
|
+
|
|
450
|
+
# Determine final project location
|
|
451
|
+
if use_current_dir:
|
|
452
|
+
final_project_path = os.getcwd()
|
|
453
|
+
else:
|
|
454
|
+
final_project_path = os.path.join(project_path, project_name)
|
|
455
|
+
|
|
456
|
+
# Initialize project with enhanced feedback
|
|
457
|
+
clear_screen()
|
|
458
|
+
print_banner()
|
|
459
|
+
print_menu_header("CREATING PROJECT", f"Setting up {project_name}")
|
|
460
|
+
|
|
461
|
+
print(f"\n{Fore.GREEN}🚀 Initializing {project_type} project with {technology}...{Style.RESET_ALL}")
|
|
462
|
+
|
|
463
|
+
# Execute project creation first (to create requirements.txt)
|
|
464
|
+
try:
|
|
465
|
+
if technology == "django":
|
|
466
|
+
if use_current_dir:
|
|
467
|
+
init_drf_project_in_current_dir(project_name)
|
|
468
|
+
else:
|
|
469
|
+
init_drf_project(project_path, project_name)
|
|
470
|
+
|
|
471
|
+
# Now show animated dependency installation with real requirements.txt
|
|
472
|
+
requirements_path = os.path.join(final_project_path, "requirements.txt")
|
|
473
|
+
if os.path.exists(requirements_path):
|
|
474
|
+
animate_dependency_install(requirements_path)
|
|
475
|
+
elif technology == "react":
|
|
476
|
+
if use_current_dir:
|
|
477
|
+
init_react_project_in_current_dir(project_name)
|
|
478
|
+
else:
|
|
479
|
+
init_react_project(project_path, project_name)
|
|
480
|
+
elif technology == "react-admin":
|
|
481
|
+
if use_current_dir:
|
|
482
|
+
init_react_admin_project_in_current_dir(project_name)
|
|
483
|
+
else:
|
|
484
|
+
init_react_admin_project(project_path, project_name)
|
|
485
|
+
elif technology == "nextjs":
|
|
486
|
+
if use_current_dir:
|
|
487
|
+
init_nextjs_project_in_current_dir(project_name)
|
|
488
|
+
else:
|
|
489
|
+
init_nextjs_project(project_path, project_name)
|
|
490
|
+
elif technology == "flutter":
|
|
491
|
+
if use_current_dir:
|
|
492
|
+
init_flutter_project_in_current_dir(project_name)
|
|
493
|
+
else:
|
|
494
|
+
init_flutter_project(project_path, project_name)
|
|
495
|
+
else:
|
|
496
|
+
print_error(f"Technology '{technology}' is not yet implemented!")
|
|
497
|
+
sys.exit(1)
|
|
498
|
+
|
|
499
|
+
# Ask about authentication app for Django projects
|
|
500
|
+
if technology == "django":
|
|
501
|
+
if ask_for_auth_app():
|
|
502
|
+
create_authentication_app(final_project_path, project_name)
|
|
503
|
+
|
|
504
|
+
# Success screen
|
|
505
|
+
show_success_screen(project_name, project_type, technology, final_project_path)
|
|
506
|
+
|
|
507
|
+
except Exception as e:
|
|
508
|
+
print_error(f"Project creation failed: {str(e)}")
|
|
509
|
+
sys.exit(1)
|
|
510
|
+
|
|
511
|
+
def ask_for_auth_app():
|
|
512
|
+
"""Ask user if they want to create an authentication app."""
|
|
513
|
+
clear_screen()
|
|
514
|
+
print_banner()
|
|
515
|
+
print_menu_header("AUTHENTICATION SETUP", "Add a dedicated authentication app?")
|
|
516
|
+
|
|
517
|
+
print(f"\n{Fore.CYAN}🔐 Authentication App Features:{Style.RESET_ALL}")
|
|
518
|
+
print(f" {Fore.WHITE}• Custom User model with AbstractUser{Style.RESET_ALL}")
|
|
519
|
+
print(f" {Fore.WHITE}• JWT Authentication (login, register, refresh){Style.RESET_ALL}")
|
|
520
|
+
print(f" {Fore.WHITE}• Profile management (me, update-profile){Style.RESET_ALL}")
|
|
521
|
+
print(f" {Fore.WHITE}• Password management (change, reset){Style.RESET_ALL}")
|
|
522
|
+
print(f" {Fore.WHITE}• Ready-to-use serializers and views{Style.RESET_ALL}")
|
|
523
|
+
|
|
524
|
+
options = [
|
|
525
|
+
{'label': '✅ Yes, create authentication app', 'value': True},
|
|
526
|
+
{'label': '❌ No, skip authentication setup', 'value': False}
|
|
527
|
+
]
|
|
528
|
+
|
|
529
|
+
return interactive_menu("AUTHENTICATION CHOICE", options, "Would you like to add authentication?")
|
|
530
|
+
|
|
531
|
+
def create_authentication_app(project_path, project_name):
|
|
532
|
+
"""Create a complete authentication app with User model, serializers, and views."""
|
|
533
|
+
animate_loading("Creating authentication app", 1.0)
|
|
534
|
+
|
|
535
|
+
# Create authentication app directory inside apps/
|
|
536
|
+
auth_app_path = os.path.join(project_path, "apps", "authentication")
|
|
537
|
+
os.makedirs(auth_app_path, exist_ok=True)
|
|
538
|
+
|
|
539
|
+
# Create __init__.py
|
|
540
|
+
with open(os.path.join(auth_app_path, "__init__.py"), "w") as f:
|
|
541
|
+
f.write("")
|
|
542
|
+
|
|
543
|
+
# Copy template files from auth_app templates directory
|
|
544
|
+
template_dir = os.path.join(os.path.dirname(__file__), "templates", "auth_app")
|
|
545
|
+
|
|
546
|
+
template_files = {
|
|
547
|
+
'apps.py': 'apps.py',
|
|
548
|
+
'models.py': 'models.py',
|
|
549
|
+
'serializers.py': 'serializers.py',
|
|
550
|
+
'views.py': 'views.py',
|
|
551
|
+
'urls.py': 'urls.py',
|
|
552
|
+
'admin.py': 'admin.py',
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
created_files = []
|
|
556
|
+
for dest_file, template_file in template_files.items():
|
|
557
|
+
template_path = os.path.join(template_dir, template_file)
|
|
558
|
+
if os.path.exists(template_path):
|
|
559
|
+
with open(template_path, 'r', encoding='utf-8') as f:
|
|
560
|
+
content = f.read()
|
|
561
|
+
dest_path = os.path.join(auth_app_path, dest_file)
|
|
562
|
+
with open(dest_path, 'w', encoding='utf-8') as f:
|
|
563
|
+
f.write(content)
|
|
564
|
+
created_files.append(dest_file)
|
|
565
|
+
else:
|
|
566
|
+
print_error(f"Template file not found: {template_path}")
|
|
567
|
+
|
|
568
|
+
# Update main project settings and URLs
|
|
569
|
+
update_project_for_auth(project_path, project_name)
|
|
570
|
+
|
|
571
|
+
animate_loading("Authentication app created successfully", 1.0)
|
|
572
|
+
|
|
573
|
+
# Print confirmation that persists through screen clears
|
|
574
|
+
print_success(f"Authentication app created at: apps/authentication/")
|
|
575
|
+
print_info(f"Files created: {', '.join(created_files)}")
|
|
576
|
+
print_info(f"Settings updated: INSTALLED_APPS + 'apps.authentication'")
|
|
577
|
+
print_info(f"URLs updated: path('auth/', include('apps.authentication.urls'))")
|
|
578
|
+
|
|
579
|
+
def update_project_for_auth(project_path, project_name):
|
|
580
|
+
"""Update main project settings and URLs to include authentication app."""
|
|
581
|
+
# Update settings/base.py (generated projects use settings/base.py, not settings.py)
|
|
582
|
+
settings_path = os.path.join(project_path, project_name, "settings", "base.py")
|
|
583
|
+
if os.path.exists(settings_path):
|
|
584
|
+
with open(settings_path, "r") as f:
|
|
585
|
+
settings_content = f.read()
|
|
586
|
+
|
|
587
|
+
# Add authentication app to INSTALLED_APPS
|
|
588
|
+
if "'apps.authentication'," not in settings_content:
|
|
589
|
+
settings_content = settings_content.replace(
|
|
590
|
+
"INSTALLED_APPS = [",
|
|
591
|
+
"INSTALLED_APPS = [\n 'apps.authentication',"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# Add AUTH_USER_MODEL setting
|
|
595
|
+
if "AUTH_USER_MODEL" not in settings_content:
|
|
596
|
+
settings_content += "\n\n# Custom User Model\nAUTH_USER_MODEL = 'apps.authentication.User'\n"
|
|
597
|
+
|
|
598
|
+
# Add email settings placeholder
|
|
599
|
+
if "EMAIL_BACKEND" not in settings_content:
|
|
600
|
+
settings_content += """
|
|
601
|
+
# Email Settings (configure for production)
|
|
602
|
+
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
603
|
+
DEFAULT_FROM_EMAIL = 'noreply@yourproject.com'
|
|
604
|
+
FRONTEND_URL = 'http://localhost:3000' # Update with your frontend URL
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
with open(settings_path, "w") as f:
|
|
608
|
+
f.write(settings_content)
|
|
609
|
+
|
|
610
|
+
# Update main URLs
|
|
611
|
+
main_urls_path = os.path.join(project_path, project_name, "urls.py")
|
|
612
|
+
if os.path.exists(main_urls_path):
|
|
613
|
+
with open(main_urls_path, "r") as f:
|
|
614
|
+
urls_content = f.read()
|
|
615
|
+
|
|
616
|
+
# Add authentication URLs
|
|
617
|
+
if "path('auth/', include('apps.authentication.urls'))," not in urls_content:
|
|
618
|
+
# Add include import if not present
|
|
619
|
+
if "from django.urls import path, include" not in urls_content:
|
|
620
|
+
urls_content = urls_content.replace(
|
|
621
|
+
"from django.urls import path",
|
|
622
|
+
"from django.urls import path, include"
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
# Add auth URL pattern
|
|
626
|
+
urls_content = urls_content.replace(
|
|
627
|
+
"urlpatterns = [",
|
|
628
|
+
"urlpatterns = [\n path('auth/', include('apps.authentication.urls')),"
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
with open(main_urls_path, "w") as f:
|
|
632
|
+
f.write(urls_content)
|
|
633
|
+
|
|
634
|
+
def show_success_screen(project_name, project_type, technology, project_path):
|
|
635
|
+
"""Show project creation success screen."""
|
|
636
|
+
clear_screen()
|
|
637
|
+
|
|
638
|
+
# Special success banner
|
|
639
|
+
print(f"\n{Fore.GREEN}{'▄' * 80}{Style.RESET_ALL}")
|
|
640
|
+
print(f"{Fore.GREEN}{'█' * 80}{Style.RESET_ALL}")
|
|
641
|
+
print(f"{Fore.GREEN}█{' ' * 78}█{Style.RESET_ALL}")
|
|
642
|
+
print(f"{Fore.GREEN}█{' ' * 25}🎉 PROJECT CREATED! 🎉{' ' * 26}█{Style.RESET_ALL}")
|
|
643
|
+
print(f"{Fore.GREEN}█{' ' * 78}█{Style.RESET_ALL}")
|
|
644
|
+
print(f"{Fore.GREEN}█{' ' * 15}{Fore.WHITE}🚀 {project_name} is ready for development! 🚀{' ' * 15}{Fore.GREEN}█{Style.RESET_ALL}")
|
|
645
|
+
print(f"{Fore.GREEN}█{' ' * 78}█{Style.RESET_ALL}")
|
|
646
|
+
print(f"{Fore.GREEN}{'█' * 80}{Style.RESET_ALL}")
|
|
647
|
+
print(f"{Fore.GREEN}{'▀' * 80}{Style.RESET_ALL}")
|
|
648
|
+
print()
|
|
649
|
+
|
|
650
|
+
# Check if authentication app was created
|
|
651
|
+
auth_app_exists = os.path.exists(os.path.join(project_path, "apps", "authentication"))
|
|
652
|
+
|
|
653
|
+
# Project details
|
|
654
|
+
print(f"\n{Fore.CYAN}📋 Project Details:{Style.RESET_ALL}")
|
|
655
|
+
print(f" {Fore.WHITE}📛 Name: {Fore.YELLOW}{project_name}{Style.RESET_ALL}")
|
|
656
|
+
print(f" {Fore.WHITE}🏗️ Type: {Fore.YELLOW}{project_type.title()}{Style.RESET_ALL}")
|
|
657
|
+
print(f" {Fore.WHITE}🔧 Tech: {Fore.YELLOW}{technology.title()}{Style.RESET_ALL}")
|
|
658
|
+
print(f" {Fore.WHITE}📁 Path: {Fore.YELLOW}{project_path}{Style.RESET_ALL}")
|
|
659
|
+
if auth_app_exists:
|
|
660
|
+
print(f" {Fore.WHITE}🔐 Auth: {Fore.GREEN}✅ Authentication app included{Style.RESET_ALL}")
|
|
661
|
+
|
|
662
|
+
# Next steps
|
|
663
|
+
print(f"\n{Fore.MAGENTA}🚀 Next Steps:{Style.RESET_ALL}")
|
|
664
|
+
if technology == "django":
|
|
665
|
+
print(f" {Fore.GREEN}1.{Style.RESET_ALL} {Fore.WHITE}cd {project_name}{Style.RESET_ALL}")
|
|
666
|
+
print(f" {Fore.GREEN}2.{Style.RESET_ALL} {Fore.WHITE}python manage.py runserver{Style.RESET_ALL}")
|
|
667
|
+
print(f" {Fore.GREEN}3.{Style.RESET_ALL} {Fore.WHITE}Visit http://127.0.0.1:8000/docs/ for API docs{Style.RESET_ALL}")
|
|
668
|
+
print(f" {Fore.GREEN}4.{Style.RESET_ALL} {Fore.WHITE}Use 'ntc create app <name>' to add new apps{Style.RESET_ALL}")
|
|
669
|
+
elif technology == "nextjs":
|
|
670
|
+
print(f" {Fore.GREEN}1.{Style.RESET_ALL} {Fore.WHITE}cd {project_name}{Style.RESET_ALL}")
|
|
671
|
+
print(f" {Fore.GREEN}2.{Style.RESET_ALL} {Fore.WHITE}npm install{Style.RESET_ALL}")
|
|
672
|
+
print(f" {Fore.GREEN}3.{Style.RESET_ALL} {Fore.WHITE}npm run dev{Style.RESET_ALL}")
|
|
673
|
+
print(f" {Fore.GREEN}4.{Style.RESET_ALL} {Fore.WHITE}Visit http://localhost:3000{Style.RESET_ALL}")
|
|
674
|
+
elif technology == "react":
|
|
675
|
+
print(f" {Fore.GREEN}1.{Style.RESET_ALL} {Fore.WHITE}cd {project_name}{Style.RESET_ALL}")
|
|
676
|
+
print(f" {Fore.GREEN}2.{Style.RESET_ALL} {Fore.WHITE}npm install{Style.RESET_ALL}")
|
|
677
|
+
print(f" {Fore.GREEN}3.{Style.RESET_ALL} {Fore.WHITE}npm run dev{Style.RESET_ALL}")
|
|
678
|
+
print(f" {Fore.GREEN}4.{Style.RESET_ALL} {Fore.WHITE}Visit http://localhost:5173{Style.RESET_ALL}")
|
|
679
|
+
elif technology == "react-admin":
|
|
680
|
+
print(f" {Fore.GREEN}1.{Style.RESET_ALL} {Fore.WHITE}cd {project_name}{Style.RESET_ALL}")
|
|
681
|
+
print(f" {Fore.GREEN}2.{Style.RESET_ALL} {Fore.WHITE}npm install{Style.RESET_ALL}")
|
|
682
|
+
print(f" {Fore.GREEN}3.{Style.RESET_ALL} {Fore.WHITE}npm run dev{Style.RESET_ALL}")
|
|
683
|
+
print(f" {Fore.GREEN}4.{Style.RESET_ALL} {Fore.WHITE}Visit http://localhost:5173{Style.RESET_ALL}")
|
|
684
|
+
elif technology == "flutter":
|
|
685
|
+
print(f" {Fore.GREEN}1.{Style.RESET_ALL} {Fore.WHITE}cd {project_name}{Style.RESET_ALL}")
|
|
686
|
+
print(f" {Fore.GREEN}2.{Style.RESET_ALL} {Fore.WHITE}flutter pub get{Style.RESET_ALL}")
|
|
687
|
+
print(f" {Fore.GREEN}3.{Style.RESET_ALL} {Fore.WHITE}flutter run{Style.RESET_ALL}")
|
|
688
|
+
print(f" {Fore.GREEN}4.{Style.RESET_ALL} {Fore.WHITE}App will run on connected device/emulator{Style.RESET_ALL}")
|
|
689
|
+
|
|
690
|
+
# Additional resources
|
|
691
|
+
print(f"\n{Fore.BLUE}📚 Resources:{Style.RESET_ALL}")
|
|
692
|
+
print(f" {Fore.WHITE}• Check the README.md for detailed setup instructions{Style.RESET_ALL}")
|
|
693
|
+
print(f" {Fore.WHITE}• Explore the .knowledge/ directory for AI documentation{Style.RESET_ALL}")
|
|
694
|
+
print(f" {Fore.WHITE}• Use 'ntc --help' for more commands{Style.RESET_ALL}")
|
|
695
|
+
|
|
696
|
+
print(f"\n{Fore.YELLOW}Happy coding! 🚀{Style.RESET_ALL}")
|
|
697
|
+
|
|
698
|
+
# Wait for user acknowledgment
|
|
699
|
+
print(f"\n{Fore.CYAN}Press any key to exit...{Style.RESET_ALL}")
|
|
700
|
+
get_key()
|
|
701
|
+
|
|
702
|
+
def main():
|
|
703
|
+
parser = argparse.ArgumentParser(
|
|
704
|
+
description="NTC - Nexent Toolkit Console",
|
|
705
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
706
|
+
epilog="""
|
|
707
|
+
Examples:
|
|
708
|
+
ntc init # Interactive mode with all options
|
|
709
|
+
ntc init --technology django # Quick Django project
|
|
710
|
+
ntc init --technology react # Quick React project
|
|
711
|
+
ntc init --technology nextjs # Quick Next.js project
|
|
712
|
+
ntc init --technology flutter # Quick Flutter project
|
|
713
|
+
ntc init --frontend react # Choose React frontend, ask for backend
|
|
714
|
+
ntc compose myproject # Compose full-stack project with multiple components
|
|
715
|
+
ntc start # Start all services from ntc.yml configuration
|
|
716
|
+
ntc create app myapp # Create new Django app with NTC structure
|
|
717
|
+
ntc drf init path name # Legacy Django REST Framework initialization
|
|
718
|
+
"""
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
722
|
+
|
|
723
|
+
# Enhanced init command
|
|
724
|
+
init_parser = subparsers.add_parser("init", help="Interactive project initialization")
|
|
725
|
+
init_parser.add_argument('--technology', '-t', type=str,
|
|
726
|
+
choices=['django', 'react', 'react-admin', 'nextjs', 'flutter'],
|
|
727
|
+
help='Technology to use (django, react, react-admin, nextjs, flutter)')
|
|
728
|
+
init_parser.add_argument("--backend",
|
|
729
|
+
choices=["django", "drf", "laravel"],
|
|
730
|
+
help="Backend technology")
|
|
731
|
+
init_parser.add_argument("--frontend",
|
|
732
|
+
choices=["react", "react-admin", "nextjs", "flutter"],
|
|
733
|
+
help="Frontend technology")
|
|
734
|
+
|
|
735
|
+
# Create command for apps
|
|
736
|
+
create_parser = subparsers.add_parser("create", help="Create new components")
|
|
737
|
+
create_subparsers = create_parser.add_subparsers(dest="create_type", help="Type of component to create")
|
|
738
|
+
|
|
739
|
+
# Create app subcommand
|
|
740
|
+
app_parser = create_subparsers.add_parser("app", help="Create new Django app with NTC structure")
|
|
741
|
+
app_parser.add_argument("app_name", help="Name of the app to create")
|
|
742
|
+
|
|
743
|
+
# Legacy DRF command for backward compatibility
|
|
744
|
+
drf_parser = subparsers.add_parser("drf", help="Django REST Framework commands (legacy)")
|
|
745
|
+
drf_subparsers = drf_parser.add_subparsers(dest="drf_action")
|
|
746
|
+
drf_init_parser = drf_subparsers.add_parser("init", help="Initialize DRF project")
|
|
747
|
+
drf_init_parser.add_argument("path", help="Base path to create the project")
|
|
748
|
+
drf_init_parser.add_argument("name", help="Project name")
|
|
749
|
+
|
|
750
|
+
# Composer command for full-stack project composition
|
|
751
|
+
composer_parser = subparsers.add_parser("compose", help="Compose a full-stack project with multiple components")
|
|
752
|
+
composer_parser.add_argument("project_name", help="Name of the project")
|
|
753
|
+
composer_parser.add_argument("--path", help="Base path to create the project (default: current directory)")
|
|
754
|
+
|
|
755
|
+
# Start services command
|
|
756
|
+
start_parser = subparsers.add_parser("start", help="Start all services defined in ntc.yml")
|
|
757
|
+
start_parser.add_argument("--path", help="Path to project root directory (default: current directory)")
|
|
758
|
+
|
|
759
|
+
args = parser.parse_args()
|
|
760
|
+
|
|
761
|
+
if args.command == "init":
|
|
762
|
+
# Determine project type and technology from arguments
|
|
763
|
+
project_type = None
|
|
764
|
+
technology = None
|
|
765
|
+
ask_for_other = False
|
|
766
|
+
|
|
767
|
+
if args.technology:
|
|
768
|
+
# Direct technology specified - skip both project type and technology selection
|
|
769
|
+
if args.technology in ["django", "drf", "laravel"]:
|
|
770
|
+
project_type = "backend"
|
|
771
|
+
technology = "django" if args.technology == "drf" else args.technology
|
|
772
|
+
else: # react, react-admin, next, vue
|
|
773
|
+
project_type = "frontend"
|
|
774
|
+
technology = args.technology
|
|
775
|
+
elif args.backend:
|
|
776
|
+
# Backend specified, but we still need to ask what kind of backend
|
|
777
|
+
project_type = "backend"
|
|
778
|
+
if args.backend == "drf":
|
|
779
|
+
technology = "django"
|
|
780
|
+
else:
|
|
781
|
+
technology = args.backend
|
|
782
|
+
elif args.frontend:
|
|
783
|
+
# Frontend specified, but we still need to ask what kind of frontend
|
|
784
|
+
project_type = "frontend"
|
|
785
|
+
technology = args.frontend
|
|
786
|
+
|
|
787
|
+
# Call enhanced interactive init with pre-selected options
|
|
788
|
+
interactive_init(preset_project_type=project_type, preset_technology=technology)
|
|
789
|
+
elif args.command == "create" and args.create_type == "app":
|
|
790
|
+
create_app_structure(args.app_name)
|
|
791
|
+
elif args.command == "drf" and args.drf_action == "init":
|
|
792
|
+
# Legacy support
|
|
793
|
+
init_drf_project(args.path, args.name)
|
|
794
|
+
elif args.command == "compose":
|
|
795
|
+
# Composer for full-stack project composition
|
|
796
|
+
if args.path:
|
|
797
|
+
compose_project(args.project_name, args.path)
|
|
798
|
+
else:
|
|
799
|
+
compose_project(args.project_name)
|
|
800
|
+
elif args.command == "start":
|
|
801
|
+
# Start services from ntc.yml configuration
|
|
802
|
+
if args.path:
|
|
803
|
+
start_services(args.path)
|
|
804
|
+
else:
|
|
805
|
+
start_services()
|
|
806
|
+
else:
|
|
807
|
+
# Show help if no command provided
|
|
808
|
+
print_banner()
|
|
809
|
+
print(f"{Fore.CYAN}💡 Quick Start:{Style.RESET_ALL}")
|
|
810
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc init'{Style.RESET_ALL} {Fore.WHITE}for interactive setup{Style.RESET_ALL}")
|
|
811
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc init django'{Style.RESET_ALL} {Fore.WHITE}for Django project{Style.RESET_ALL}")
|
|
812
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc init react-admin'{Style.RESET_ALL} {Fore.WHITE}for React Admin Dashboard{Style.RESET_ALL}")
|
|
813
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc compose myproject'{Style.RESET_ALL} {Fore.WHITE}for full-stack composition{Style.RESET_ALL}")
|
|
814
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc start'{Style.RESET_ALL} {Fore.WHITE}to start all services{Style.RESET_ALL}")
|
|
815
|
+
print(f" {Fore.WHITE}• Run {Fore.GREEN}'ntc create app <name>'{Style.RESET_ALL} {Fore.WHITE}to add Django apps{Style.RESET_ALL}")
|
|
816
|
+
print()
|
|
817
|
+
parser.print_help()
|
|
818
|
+
|
|
819
|
+
if __name__ == "__main__":
|
|
820
|
+
main()
|