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/__init__.py
ADDED
ntc/app_creator.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from ntc.utils.output import print_success, print_error, print_info
|
|
4
|
+
from ntc.templates.drf_structure.main import sanitize_project_name
|
|
5
|
+
|
|
6
|
+
def get_app_template_content(template_name, app_name):
|
|
7
|
+
"""Load app template content and replace variables."""
|
|
8
|
+
template_path = os.path.join(
|
|
9
|
+
os.path.dirname(__file__),
|
|
10
|
+
'templates', 'drf_structure', 'templates', 'apps', template_name
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
if not os.path.exists(template_path):
|
|
14
|
+
# Fallback to inline content if template not found
|
|
15
|
+
return get_fallback_content(template_name, app_name)
|
|
16
|
+
|
|
17
|
+
with open(template_path, 'r', encoding='utf-8') as f:
|
|
18
|
+
content = f.read()
|
|
19
|
+
|
|
20
|
+
# Replace template variables
|
|
21
|
+
content = content.replace('{app_name}', app_name)
|
|
22
|
+
content = content.replace('{app_name_title}', app_name.replace('_', '').title())
|
|
23
|
+
|
|
24
|
+
return content
|
|
25
|
+
|
|
26
|
+
def get_fallback_content(template_name, app_name):
|
|
27
|
+
"""Fallback content if template files are not found."""
|
|
28
|
+
fallbacks = {
|
|
29
|
+
'app_admin.py': '''from django.contrib import admin
|
|
30
|
+
|
|
31
|
+
# Register your models here.
|
|
32
|
+
''',
|
|
33
|
+
'app_apps.py': f'''from django.apps import AppConfig
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class {app_name.replace('_', '').title()}Config(AppConfig):
|
|
37
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
38
|
+
name = 'apps.{app_name}'
|
|
39
|
+
''',
|
|
40
|
+
'app_models.py': '''from django.db import models
|
|
41
|
+
from apps.common.models import TimeStampedModel
|
|
42
|
+
|
|
43
|
+
# Create your models here.
|
|
44
|
+
''',
|
|
45
|
+
'app_urls.py': f'''from django.urls import path
|
|
46
|
+
from . import views
|
|
47
|
+
|
|
48
|
+
app_name = '{app_name}'
|
|
49
|
+
|
|
50
|
+
urlpatterns = [
|
|
51
|
+
path('hello/', views.hello_world, name='hello_world'),
|
|
52
|
+
# Add your other URL patterns here
|
|
53
|
+
]
|
|
54
|
+
''',
|
|
55
|
+
'app_views.py': f'''from rest_framework import viewsets, status
|
|
56
|
+
from rest_framework.decorators import api_view
|
|
57
|
+
from rest_framework.response import Response
|
|
58
|
+
from core.response_wrapper import success_response, error_response
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@api_view(['GET'])
|
|
62
|
+
def hello_world(request):
|
|
63
|
+
"""Test view that returns Hello World message."""
|
|
64
|
+
return success_response(
|
|
65
|
+
data={{"message": "Hello World from {app_name} app!"}},
|
|
66
|
+
message="Welcome to {app_name} API"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Create your other views here.
|
|
71
|
+
''',
|
|
72
|
+
}
|
|
73
|
+
return fallbacks.get(template_name, '')
|
|
74
|
+
|
|
75
|
+
def create_app_structure(app_name):
|
|
76
|
+
"""Create the NTC app directory structure."""
|
|
77
|
+
# Sanitize app name using the same function as project names
|
|
78
|
+
original_name = app_name
|
|
79
|
+
app_name = sanitize_project_name(app_name)
|
|
80
|
+
|
|
81
|
+
if app_name != original_name:
|
|
82
|
+
print_info(f"App name sanitized: '{original_name}' -> '{app_name}'")
|
|
83
|
+
|
|
84
|
+
# Check if we're in a Django project root
|
|
85
|
+
if not os.path.exists('manage.py'):
|
|
86
|
+
print_error("Not in a Django project root directory. Please run this command from your project root.")
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
# Check if apps directory exists
|
|
90
|
+
if not os.path.exists('apps'):
|
|
91
|
+
print_info("Creating apps directory...")
|
|
92
|
+
os.makedirs('apps', exist_ok=True)
|
|
93
|
+
# Create apps/__init__.py
|
|
94
|
+
with open('apps/__init__.py', 'w') as f:
|
|
95
|
+
f.write('')
|
|
96
|
+
|
|
97
|
+
base_path = os.path.join('apps', app_name)
|
|
98
|
+
|
|
99
|
+
# Check if app already exists
|
|
100
|
+
if os.path.exists(base_path):
|
|
101
|
+
print_error(f"App '{app_name}' already exists in apps/{app_name}/")
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
print_info(f"Creating app structure for '{app_name}'...")
|
|
105
|
+
|
|
106
|
+
# Create directories
|
|
107
|
+
directories = [
|
|
108
|
+
base_path,
|
|
109
|
+
os.path.join(base_path, 'permissions'),
|
|
110
|
+
os.path.join(base_path, 'serializers'),
|
|
111
|
+
os.path.join(base_path, 'selectors'),
|
|
112
|
+
os.path.join(base_path, 'services'),
|
|
113
|
+
os.path.join(base_path, 'socket'),
|
|
114
|
+
os.path.join(base_path, 'tasks'),
|
|
115
|
+
os.path.join(base_path, 'tests'),
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
for directory in directories:
|
|
119
|
+
os.makedirs(directory, exist_ok=True)
|
|
120
|
+
# Create __init__.py in each directory
|
|
121
|
+
init_file = os.path.join(directory, '__init__.py')
|
|
122
|
+
with open(init_file, 'w') as f:
|
|
123
|
+
f.write('')
|
|
124
|
+
|
|
125
|
+
# Create main app files
|
|
126
|
+
files_content = {
|
|
127
|
+
'admin.py': get_app_template_content('app_admin.py', app_name),
|
|
128
|
+
'apps.py': get_app_template_content('app_apps.py', app_name),
|
|
129
|
+
'models.py': get_app_template_content('app_models.py', app_name),
|
|
130
|
+
'urls.py': get_app_template_content('app_urls.py', app_name),
|
|
131
|
+
'views.py': get_app_template_content('app_views.py', app_name),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for filename, content in files_content.items():
|
|
135
|
+
file_path = os.path.join(base_path, filename)
|
|
136
|
+
with open(file_path, 'w') as f:
|
|
137
|
+
f.write(content)
|
|
138
|
+
|
|
139
|
+
print_success(f"App '{app_name}' created successfully!")
|
|
140
|
+
|
|
141
|
+
# Add to INSTALLED_APPS
|
|
142
|
+
if add_to_installed_apps(app_name):
|
|
143
|
+
print_success(f"Added 'apps.{app_name}' to INSTALLED_APPS")
|
|
144
|
+
else:
|
|
145
|
+
print_info(f"Please manually add 'apps.{app_name}' to INSTALLED_APPS in your settings")
|
|
146
|
+
|
|
147
|
+
# Add to main URLs
|
|
148
|
+
if add_to_main_urls(app_name):
|
|
149
|
+
print_success(f"Added app URLs to main urls.py")
|
|
150
|
+
else:
|
|
151
|
+
print_info(f"Please manually add: path('{app_name}/', include('apps.{app_name}.urls')) to your main urls.py")
|
|
152
|
+
|
|
153
|
+
print_info("Next steps:")
|
|
154
|
+
print_info(f" 1. Test the app: GET /{app_name}/hello/ (returns Hello World)")
|
|
155
|
+
print_info(f" 2. Run 'python manage.py makemigrations {app_name}' after creating models")
|
|
156
|
+
print_info(f" 3. Start building your API endpoints in apps/{app_name}/views.py")
|
|
157
|
+
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
def add_to_installed_apps(app_name):
|
|
161
|
+
"""Add the new app to INSTALLED_APPS in settings."""
|
|
162
|
+
# Find settings files
|
|
163
|
+
settings_files = []
|
|
164
|
+
|
|
165
|
+
# Look for settings in common locations
|
|
166
|
+
possible_settings = [
|
|
167
|
+
'settings.py',
|
|
168
|
+
'settings/base.py',
|
|
169
|
+
'settings/dev.py',
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# Find project directory (should contain settings)
|
|
173
|
+
for root, dirs, files in os.walk('.'):
|
|
174
|
+
if 'settings.py' in files or 'settings' in dirs:
|
|
175
|
+
for settings_file in possible_settings:
|
|
176
|
+
settings_path = os.path.join(root, settings_file)
|
|
177
|
+
if os.path.exists(settings_path):
|
|
178
|
+
settings_files.append(settings_path)
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
if not settings_files:
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
# Use the first settings file found (usually base.py or settings.py)
|
|
185
|
+
settings_file = settings_files[0]
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
with open(settings_file, 'r', encoding='utf-8') as f:
|
|
189
|
+
content = f.read()
|
|
190
|
+
|
|
191
|
+
# Check if app is already in INSTALLED_APPS
|
|
192
|
+
if f"'apps.{app_name}'" in content or f'"apps.{app_name}"' in content:
|
|
193
|
+
return True # Already added
|
|
194
|
+
|
|
195
|
+
# Find INSTALLED_APPS and add the new app
|
|
196
|
+
import re
|
|
197
|
+
|
|
198
|
+
# Pattern to match INSTALLED_APPS
|
|
199
|
+
pattern = r'(INSTALLED_APPS\s*=\s*\[)(.*?)(\])'
|
|
200
|
+
match = re.search(pattern, content, re.DOTALL)
|
|
201
|
+
|
|
202
|
+
if match:
|
|
203
|
+
start, apps_content, end = match.groups()
|
|
204
|
+
|
|
205
|
+
# Add the new app to the end of the list
|
|
206
|
+
new_app_line = f" 'apps.{app_name}',\n"
|
|
207
|
+
|
|
208
|
+
# Insert before the closing bracket
|
|
209
|
+
new_content = content.replace(
|
|
210
|
+
match.group(0),
|
|
211
|
+
f"{start}{apps_content.rstrip()}\n{new_app_line}{end}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
with open(settings_file, 'w', encoding='utf-8') as f:
|
|
215
|
+
f.write(new_content)
|
|
216
|
+
|
|
217
|
+
return True
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
print_error(f"Failed to update INSTALLED_APPS: {e}")
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
def add_to_main_urls(app_name):
|
|
226
|
+
"""Add the new app URLs to the main urls.py file."""
|
|
227
|
+
# Find main urls.py
|
|
228
|
+
urls_files = []
|
|
229
|
+
|
|
230
|
+
# Look for main urls.py (usually in project root or project directory)
|
|
231
|
+
for root, dirs, files in os.walk('.'):
|
|
232
|
+
if 'urls.py' in files:
|
|
233
|
+
urls_path = os.path.join(root, 'urls.py')
|
|
234
|
+
# Skip app-specific urls.py files
|
|
235
|
+
if 'apps' not in root:
|
|
236
|
+
urls_files.append(urls_path)
|
|
237
|
+
|
|
238
|
+
if not urls_files:
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
# Use the first main urls.py found
|
|
242
|
+
urls_file = urls_files[0]
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
with open(urls_file, 'r', encoding='utf-8') as f:
|
|
246
|
+
content = f.read()
|
|
247
|
+
|
|
248
|
+
# Check if app URLs are already included
|
|
249
|
+
if f"'{app_name}/'" in content or f'"{app_name}/"' in content:
|
|
250
|
+
return True # Already added
|
|
251
|
+
|
|
252
|
+
# Check if include is imported
|
|
253
|
+
if 'from django.urls import' in content:
|
|
254
|
+
if 'include' not in content:
|
|
255
|
+
# Add include to existing import
|
|
256
|
+
content = content.replace(
|
|
257
|
+
'from django.urls import',
|
|
258
|
+
'from django.urls import include,'
|
|
259
|
+
).replace('include,', 'include, ')
|
|
260
|
+
else:
|
|
261
|
+
# Add the import at the top
|
|
262
|
+
lines = content.split('\n')
|
|
263
|
+
for i, line in enumerate(lines):
|
|
264
|
+
if line.startswith('from django.'):
|
|
265
|
+
lines.insert(i + 1, 'from django.urls import path, include')
|
|
266
|
+
break
|
|
267
|
+
content = '\n'.join(lines)
|
|
268
|
+
|
|
269
|
+
# Find urlpatterns and add the new app
|
|
270
|
+
import re
|
|
271
|
+
|
|
272
|
+
pattern = r'(urlpatterns\s*=\s*\[)(.*?)(\])'
|
|
273
|
+
match = re.search(pattern, content, re.DOTALL)
|
|
274
|
+
|
|
275
|
+
if match:
|
|
276
|
+
start, urls_content, end = match.groups()
|
|
277
|
+
|
|
278
|
+
# Add the new app URL pattern
|
|
279
|
+
new_url_line = f" path('{app_name}/', include('apps.{app_name}.urls')),\n"
|
|
280
|
+
|
|
281
|
+
# Insert before the closing bracket
|
|
282
|
+
new_content = content.replace(
|
|
283
|
+
match.group(0),
|
|
284
|
+
f"{start}{urls_content.rstrip()}\n{new_url_line}{end}"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
with open(urls_file, 'w', encoding='utf-8') as f:
|
|
288
|
+
f.write(new_content)
|
|
289
|
+
|
|
290
|
+
return True
|
|
291
|
+
|
|
292
|
+
except Exception as e:
|
|
293
|
+
print_error(f"Failed to update main urls.py: {e}")
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
return False
|
ntc/cli.py
ADDED