nc1709 1.15.4__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.
- nc1709/__init__.py +13 -0
- nc1709/agent/__init__.py +36 -0
- nc1709/agent/core.py +505 -0
- nc1709/agent/mcp_bridge.py +245 -0
- nc1709/agent/permissions.py +298 -0
- nc1709/agent/tools/__init__.py +21 -0
- nc1709/agent/tools/base.py +440 -0
- nc1709/agent/tools/bash_tool.py +367 -0
- nc1709/agent/tools/file_tools.py +454 -0
- nc1709/agent/tools/notebook_tools.py +516 -0
- nc1709/agent/tools/search_tools.py +322 -0
- nc1709/agent/tools/task_tool.py +284 -0
- nc1709/agent/tools/web_tools.py +555 -0
- nc1709/agents/__init__.py +17 -0
- nc1709/agents/auto_fix.py +506 -0
- nc1709/agents/test_generator.py +507 -0
- nc1709/checkpoints.py +372 -0
- nc1709/cli.py +3380 -0
- nc1709/cli_ui.py +1080 -0
- nc1709/cognitive/__init__.py +149 -0
- nc1709/cognitive/anticipation.py +594 -0
- nc1709/cognitive/context_engine.py +1046 -0
- nc1709/cognitive/council.py +824 -0
- nc1709/cognitive/learning.py +761 -0
- nc1709/cognitive/router.py +583 -0
- nc1709/cognitive/system.py +519 -0
- nc1709/config.py +155 -0
- nc1709/custom_commands.py +300 -0
- nc1709/executor.py +333 -0
- nc1709/file_controller.py +354 -0
- nc1709/git_integration.py +308 -0
- nc1709/github_integration.py +477 -0
- nc1709/image_input.py +446 -0
- nc1709/linting.py +519 -0
- nc1709/llm_adapter.py +667 -0
- nc1709/logger.py +192 -0
- nc1709/mcp/__init__.py +18 -0
- nc1709/mcp/client.py +370 -0
- nc1709/mcp/manager.py +407 -0
- nc1709/mcp/protocol.py +210 -0
- nc1709/mcp/server.py +473 -0
- nc1709/memory/__init__.py +20 -0
- nc1709/memory/embeddings.py +325 -0
- nc1709/memory/indexer.py +474 -0
- nc1709/memory/sessions.py +432 -0
- nc1709/memory/vector_store.py +451 -0
- nc1709/models/__init__.py +86 -0
- nc1709/models/detector.py +377 -0
- nc1709/models/formats.py +315 -0
- nc1709/models/manager.py +438 -0
- nc1709/models/registry.py +497 -0
- nc1709/performance/__init__.py +343 -0
- nc1709/performance/cache.py +705 -0
- nc1709/performance/pipeline.py +611 -0
- nc1709/performance/tiering.py +543 -0
- nc1709/plan_mode.py +362 -0
- nc1709/plugins/__init__.py +17 -0
- nc1709/plugins/agents/__init__.py +18 -0
- nc1709/plugins/agents/django_agent.py +912 -0
- nc1709/plugins/agents/docker_agent.py +623 -0
- nc1709/plugins/agents/fastapi_agent.py +887 -0
- nc1709/plugins/agents/git_agent.py +731 -0
- nc1709/plugins/agents/nextjs_agent.py +867 -0
- nc1709/plugins/base.py +359 -0
- nc1709/plugins/manager.py +411 -0
- nc1709/plugins/registry.py +337 -0
- nc1709/progress.py +443 -0
- nc1709/prompts/__init__.py +22 -0
- nc1709/prompts/agent_system.py +180 -0
- nc1709/prompts/task_prompts.py +340 -0
- nc1709/prompts/unified_prompt.py +133 -0
- nc1709/reasoning_engine.py +541 -0
- nc1709/remote_client.py +266 -0
- nc1709/shell_completions.py +349 -0
- nc1709/slash_commands.py +649 -0
- nc1709/task_classifier.py +408 -0
- nc1709/version_check.py +177 -0
- nc1709/web/__init__.py +8 -0
- nc1709/web/server.py +950 -0
- nc1709/web/templates/index.html +1127 -0
- nc1709-1.15.4.dist-info/METADATA +858 -0
- nc1709-1.15.4.dist-info/RECORD +86 -0
- nc1709-1.15.4.dist-info/WHEEL +5 -0
- nc1709-1.15.4.dist-info/entry_points.txt +2 -0
- nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
- nc1709-1.15.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django Agent for NC1709
|
|
3
|
+
Scaffolds Django projects, apps, models, views, and more
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, Any, Optional, List
|
|
9
|
+
|
|
10
|
+
from ..base import (
|
|
11
|
+
Plugin, PluginMetadata, PluginCapability,
|
|
12
|
+
ActionResult
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DjangoAgent(Plugin):
|
|
17
|
+
"""
|
|
18
|
+
Django scaffolding and development agent.
|
|
19
|
+
|
|
20
|
+
Provides:
|
|
21
|
+
- Project scaffolding
|
|
22
|
+
- App generation
|
|
23
|
+
- Model generation
|
|
24
|
+
- View generation (function and class-based)
|
|
25
|
+
- URL configuration
|
|
26
|
+
- Admin registration
|
|
27
|
+
- Serializer generation (DRF)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
METADATA = PluginMetadata(
|
|
31
|
+
name="django",
|
|
32
|
+
version="1.0.0",
|
|
33
|
+
description="Django project scaffolding and development",
|
|
34
|
+
author="NC1709 Team",
|
|
35
|
+
capabilities=[
|
|
36
|
+
PluginCapability.CODE_GENERATION,
|
|
37
|
+
PluginCapability.PROJECT_SCAFFOLDING
|
|
38
|
+
],
|
|
39
|
+
keywords=[
|
|
40
|
+
"django", "python", "model", "view", "template",
|
|
41
|
+
"admin", "orm", "rest", "drf", "serializer",
|
|
42
|
+
"migration", "backend", "web"
|
|
43
|
+
],
|
|
44
|
+
config_schema={
|
|
45
|
+
"project_path": {"type": "string", "default": "."},
|
|
46
|
+
"use_drf": {"type": "boolean", "default": True},
|
|
47
|
+
"database": {"type": "string", "enum": ["sqlite", "postgresql", "mysql"], "default": "sqlite"}
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def metadata(self) -> PluginMetadata:
|
|
53
|
+
return self.METADATA
|
|
54
|
+
|
|
55
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
56
|
+
super().__init__(config)
|
|
57
|
+
self._project_path: Optional[Path] = None
|
|
58
|
+
|
|
59
|
+
def initialize(self) -> bool:
|
|
60
|
+
"""Initialize the Django agent"""
|
|
61
|
+
self._project_path = Path(self._config.get("project_path", ".")).resolve()
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
def cleanup(self) -> None:
|
|
65
|
+
"""Cleanup resources"""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def _register_actions(self) -> None:
|
|
69
|
+
"""Register Django actions"""
|
|
70
|
+
self.register_action(
|
|
71
|
+
"scaffold",
|
|
72
|
+
self.scaffold_project,
|
|
73
|
+
"Create a new Django project structure",
|
|
74
|
+
parameters={
|
|
75
|
+
"name": {"type": "string", "required": True},
|
|
76
|
+
"with_drf": {"type": "boolean", "default": True}
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
self.register_action(
|
|
81
|
+
"app",
|
|
82
|
+
self.create_app,
|
|
83
|
+
"Generate a new Django app",
|
|
84
|
+
parameters={
|
|
85
|
+
"name": {"type": "string", "required": True}
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self.register_action(
|
|
90
|
+
"model",
|
|
91
|
+
self.create_model,
|
|
92
|
+
"Generate a Django model",
|
|
93
|
+
parameters={
|
|
94
|
+
"app": {"type": "string", "required": True},
|
|
95
|
+
"name": {"type": "string", "required": True},
|
|
96
|
+
"fields": {"type": "object", "required": True}
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.register_action(
|
|
101
|
+
"view",
|
|
102
|
+
self.create_view,
|
|
103
|
+
"Generate a Django view",
|
|
104
|
+
parameters={
|
|
105
|
+
"app": {"type": "string", "required": True},
|
|
106
|
+
"name": {"type": "string", "required": True},
|
|
107
|
+
"view_type": {"type": "string", "enum": ["function", "class", "viewset"], "default": "class"}
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self.register_action(
|
|
112
|
+
"serializer",
|
|
113
|
+
self.create_serializer,
|
|
114
|
+
"Generate a DRF serializer",
|
|
115
|
+
parameters={
|
|
116
|
+
"app": {"type": "string", "required": True},
|
|
117
|
+
"model": {"type": "string", "required": True}
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
self.register_action(
|
|
122
|
+
"analyze",
|
|
123
|
+
self.analyze_project,
|
|
124
|
+
"Analyze existing Django project structure"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def scaffold_project(
|
|
128
|
+
self,
|
|
129
|
+
name: str,
|
|
130
|
+
with_drf: bool = True
|
|
131
|
+
) -> ActionResult:
|
|
132
|
+
"""Create a new Django project structure
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
name: Project name
|
|
136
|
+
with_drf: Include Django REST Framework
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
ActionResult
|
|
140
|
+
"""
|
|
141
|
+
project_dir = self._project_path / name
|
|
142
|
+
|
|
143
|
+
if project_dir.exists():
|
|
144
|
+
return ActionResult.fail(f"Directory '{name}' already exists")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Create directory structure
|
|
148
|
+
config_dir = project_dir / name # Django puts config in project_name/project_name
|
|
149
|
+
dirs = [
|
|
150
|
+
project_dir,
|
|
151
|
+
config_dir,
|
|
152
|
+
project_dir / "apps",
|
|
153
|
+
project_dir / "templates",
|
|
154
|
+
project_dir / "static",
|
|
155
|
+
project_dir / "media",
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
for d in dirs:
|
|
159
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
|
|
161
|
+
# Create __init__.py files
|
|
162
|
+
(config_dir / "__init__.py").write_text("")
|
|
163
|
+
(project_dir / "apps" / "__init__.py").write_text("")
|
|
164
|
+
|
|
165
|
+
# Create manage.py
|
|
166
|
+
manage_content = self._generate_manage_py(name)
|
|
167
|
+
(project_dir / "manage.py").write_text(manage_content)
|
|
168
|
+
|
|
169
|
+
# Create settings.py
|
|
170
|
+
settings_content = self._generate_settings(name, with_drf)
|
|
171
|
+
(config_dir / "settings.py").write_text(settings_content)
|
|
172
|
+
|
|
173
|
+
# Create urls.py
|
|
174
|
+
urls_content = self._generate_urls(name, with_drf)
|
|
175
|
+
(config_dir / "urls.py").write_text(urls_content)
|
|
176
|
+
|
|
177
|
+
# Create wsgi.py
|
|
178
|
+
wsgi_content = self._generate_wsgi(name)
|
|
179
|
+
(config_dir / "wsgi.py").write_text(wsgi_content)
|
|
180
|
+
|
|
181
|
+
# Create asgi.py
|
|
182
|
+
asgi_content = self._generate_asgi(name)
|
|
183
|
+
(config_dir / "asgi.py").write_text(asgi_content)
|
|
184
|
+
|
|
185
|
+
# Create requirements.txt
|
|
186
|
+
requirements = self._generate_requirements(with_drf)
|
|
187
|
+
(project_dir / "requirements.txt").write_text(requirements)
|
|
188
|
+
|
|
189
|
+
# Create .env.example
|
|
190
|
+
env_content = self._generate_env_example(name)
|
|
191
|
+
(project_dir / ".env.example").write_text(env_content)
|
|
192
|
+
|
|
193
|
+
# Create .gitignore
|
|
194
|
+
gitignore = self._generate_gitignore()
|
|
195
|
+
(project_dir / ".gitignore").write_text(gitignore)
|
|
196
|
+
|
|
197
|
+
files_created = len(list(project_dir.rglob("*")))
|
|
198
|
+
|
|
199
|
+
return ActionResult.ok(
|
|
200
|
+
message=f"Created Django project '{name}' with {files_created} files",
|
|
201
|
+
data={
|
|
202
|
+
"project_path": str(project_dir),
|
|
203
|
+
"with_drf": with_drf,
|
|
204
|
+
"next_steps": [
|
|
205
|
+
f"cd {name}",
|
|
206
|
+
"python -m venv venv",
|
|
207
|
+
"source venv/bin/activate",
|
|
208
|
+
"pip install -r requirements.txt",
|
|
209
|
+
"python manage.py migrate",
|
|
210
|
+
"python manage.py runserver"
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
return ActionResult.fail(str(e))
|
|
217
|
+
|
|
218
|
+
def create_app(self, name: str) -> ActionResult:
|
|
219
|
+
"""Generate a new Django app
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
name: App name
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
ActionResult with generated structure
|
|
226
|
+
"""
|
|
227
|
+
app_dir = self._project_path / "apps" / name
|
|
228
|
+
|
|
229
|
+
if app_dir.exists():
|
|
230
|
+
return ActionResult.fail(f"App '{name}' already exists")
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
# Create app directory
|
|
234
|
+
app_dir.mkdir(parents=True, exist_ok=True)
|
|
235
|
+
(app_dir / "migrations").mkdir()
|
|
236
|
+
|
|
237
|
+
# Create __init__.py files
|
|
238
|
+
(app_dir / "__init__.py").write_text("")
|
|
239
|
+
(app_dir / "migrations" / "__init__.py").write_text("")
|
|
240
|
+
|
|
241
|
+
# Create models.py
|
|
242
|
+
models_content = self._generate_models_py()
|
|
243
|
+
(app_dir / "models.py").write_text(models_content)
|
|
244
|
+
|
|
245
|
+
# Create views.py
|
|
246
|
+
views_content = self._generate_views_py()
|
|
247
|
+
(app_dir / "views.py").write_text(views_content)
|
|
248
|
+
|
|
249
|
+
# Create urls.py
|
|
250
|
+
urls_content = self._generate_app_urls(name)
|
|
251
|
+
(app_dir / "urls.py").write_text(urls_content)
|
|
252
|
+
|
|
253
|
+
# Create admin.py
|
|
254
|
+
admin_content = self._generate_admin_py()
|
|
255
|
+
(app_dir / "admin.py").write_text(admin_content)
|
|
256
|
+
|
|
257
|
+
# Create apps.py
|
|
258
|
+
apps_content = self._generate_apps_py(name)
|
|
259
|
+
(app_dir / "apps.py").write_text(apps_content)
|
|
260
|
+
|
|
261
|
+
# Create serializers.py (for DRF)
|
|
262
|
+
serializers_content = self._generate_serializers_py()
|
|
263
|
+
(app_dir / "serializers.py").write_text(serializers_content)
|
|
264
|
+
|
|
265
|
+
# Create tests.py
|
|
266
|
+
tests_content = self._generate_tests_py(name)
|
|
267
|
+
(app_dir / "tests.py").write_text(tests_content)
|
|
268
|
+
|
|
269
|
+
return ActionResult.ok(
|
|
270
|
+
message=f"Created Django app '{name}'",
|
|
271
|
+
data={
|
|
272
|
+
"app_path": str(app_dir),
|
|
273
|
+
"files": [
|
|
274
|
+
"models.py",
|
|
275
|
+
"views.py",
|
|
276
|
+
"urls.py",
|
|
277
|
+
"admin.py",
|
|
278
|
+
"serializers.py",
|
|
279
|
+
"tests.py"
|
|
280
|
+
],
|
|
281
|
+
"note": f"Add 'apps.{name}' to INSTALLED_APPS in settings.py"
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
except Exception as e:
|
|
286
|
+
return ActionResult.fail(str(e))
|
|
287
|
+
|
|
288
|
+
def create_model(
|
|
289
|
+
self,
|
|
290
|
+
app: str,
|
|
291
|
+
name: str,
|
|
292
|
+
fields: Dict[str, str]
|
|
293
|
+
) -> ActionResult:
|
|
294
|
+
"""Generate a Django model
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
app: App name
|
|
298
|
+
name: Model name
|
|
299
|
+
fields: Dict of field_name -> field_type
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
ActionResult with generated code
|
|
303
|
+
"""
|
|
304
|
+
code = self._generate_model_code(name, fields)
|
|
305
|
+
|
|
306
|
+
return ActionResult.ok(
|
|
307
|
+
message=f"Generated model '{name}' for app '{app}'",
|
|
308
|
+
data={
|
|
309
|
+
"code": code,
|
|
310
|
+
"path": f"apps/{app}/models.py",
|
|
311
|
+
"migrations_note": "Run: python manage.py makemigrations && python manage.py migrate"
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def create_view(
|
|
316
|
+
self,
|
|
317
|
+
app: str,
|
|
318
|
+
name: str,
|
|
319
|
+
view_type: str = "class"
|
|
320
|
+
) -> ActionResult:
|
|
321
|
+
"""Generate a Django view
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
app: App name
|
|
325
|
+
name: View name
|
|
326
|
+
view_type: Type of view (function, class, viewset)
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
ActionResult with generated code
|
|
330
|
+
"""
|
|
331
|
+
if view_type == "function":
|
|
332
|
+
code = self._generate_function_view(name)
|
|
333
|
+
elif view_type == "viewset":
|
|
334
|
+
code = self._generate_viewset(name)
|
|
335
|
+
else:
|
|
336
|
+
code = self._generate_class_view(name)
|
|
337
|
+
|
|
338
|
+
return ActionResult.ok(
|
|
339
|
+
message=f"Generated {view_type} view '{name}'",
|
|
340
|
+
data={
|
|
341
|
+
"code": code,
|
|
342
|
+
"path": f"apps/{app}/views.py",
|
|
343
|
+
"type": view_type
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def create_serializer(
|
|
348
|
+
self,
|
|
349
|
+
app: str,
|
|
350
|
+
model: str
|
|
351
|
+
) -> ActionResult:
|
|
352
|
+
"""Generate a DRF serializer
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
app: App name
|
|
356
|
+
model: Model name
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
ActionResult with generated code
|
|
360
|
+
"""
|
|
361
|
+
code = self._generate_serializer_code(model)
|
|
362
|
+
|
|
363
|
+
return ActionResult.ok(
|
|
364
|
+
message=f"Generated serializer for '{model}'",
|
|
365
|
+
data={
|
|
366
|
+
"code": code,
|
|
367
|
+
"path": f"apps/{app}/serializers.py"
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
def analyze_project(self) -> ActionResult:
|
|
372
|
+
"""Analyze existing Django project structure
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
ActionResult with project analysis
|
|
376
|
+
"""
|
|
377
|
+
manage_py = self._project_path / "manage.py"
|
|
378
|
+
|
|
379
|
+
if not manage_py.exists():
|
|
380
|
+
return ActionResult.fail("No Django project found (manage.py not found)")
|
|
381
|
+
|
|
382
|
+
analysis = {
|
|
383
|
+
"project_path": str(self._project_path),
|
|
384
|
+
"apps": [],
|
|
385
|
+
"models": [],
|
|
386
|
+
"views": [],
|
|
387
|
+
"urls": []
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# Find apps
|
|
391
|
+
for apps_dir in [self._project_path / "apps", self._project_path]:
|
|
392
|
+
if apps_dir.exists():
|
|
393
|
+
for item in apps_dir.iterdir():
|
|
394
|
+
if item.is_dir() and (item / "models.py").exists():
|
|
395
|
+
app_name = item.name
|
|
396
|
+
analysis["apps"].append(app_name)
|
|
397
|
+
|
|
398
|
+
# Find models in this app
|
|
399
|
+
models_file = item / "models.py"
|
|
400
|
+
if models_file.exists():
|
|
401
|
+
content = models_file.read_text()
|
|
402
|
+
for match in re.finditer(r'class\s+(\w+)\s*\([^)]*Model[^)]*\)', content):
|
|
403
|
+
analysis["models"].append({
|
|
404
|
+
"app": app_name,
|
|
405
|
+
"name": match.group(1)
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
# Find views
|
|
409
|
+
views_file = item / "views.py"
|
|
410
|
+
if views_file.exists():
|
|
411
|
+
content = views_file.read_text()
|
|
412
|
+
for match in re.finditer(r'(?:def|class)\s+(\w+)', content):
|
|
413
|
+
analysis["views"].append({
|
|
414
|
+
"app": app_name,
|
|
415
|
+
"name": match.group(1)
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
return ActionResult.ok(
|
|
419
|
+
message=f"Analyzed Django project with {len(analysis['apps'])} apps",
|
|
420
|
+
data=analysis
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Code generation helpers
|
|
424
|
+
|
|
425
|
+
def _generate_manage_py(self, name: str) -> str:
|
|
426
|
+
"""Generate manage.py"""
|
|
427
|
+
return f'''#!/usr/bin/env python
|
|
428
|
+
"""Django's command-line utility for administrative tasks."""
|
|
429
|
+
import os
|
|
430
|
+
import sys
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def main():
|
|
434
|
+
"""Run administrative tasks."""
|
|
435
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{name}.settings')
|
|
436
|
+
try:
|
|
437
|
+
from django.core.management import execute_from_command_line
|
|
438
|
+
except ImportError as exc:
|
|
439
|
+
raise ImportError(
|
|
440
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
441
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
442
|
+
"forget to activate a virtual environment?"
|
|
443
|
+
) from exc
|
|
444
|
+
execute_from_command_line(sys.argv)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
if __name__ == '__main__':
|
|
448
|
+
main()
|
|
449
|
+
'''
|
|
450
|
+
|
|
451
|
+
def _generate_settings(self, name: str, with_drf: bool) -> str:
|
|
452
|
+
"""Generate settings.py"""
|
|
453
|
+
drf_apps = "'rest_framework'," if with_drf else ""
|
|
454
|
+
drf_settings = '''
|
|
455
|
+
REST_FRAMEWORK = {
|
|
456
|
+
'DEFAULT_PERMISSION_CLASSES': [
|
|
457
|
+
'rest_framework.permissions.IsAuthenticated',
|
|
458
|
+
],
|
|
459
|
+
'DEFAULT_AUTHENTICATION_CLASSES': [
|
|
460
|
+
'rest_framework.authentication.SessionAuthentication',
|
|
461
|
+
],
|
|
462
|
+
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
|
463
|
+
'PAGE_SIZE': 10,
|
|
464
|
+
}
|
|
465
|
+
''' if with_drf else ""
|
|
466
|
+
|
|
467
|
+
return f'''"""
|
|
468
|
+
Django settings for {name} project.
|
|
469
|
+
"""
|
|
470
|
+
import os
|
|
471
|
+
from pathlib import Path
|
|
472
|
+
|
|
473
|
+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
474
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
475
|
+
|
|
476
|
+
# SECURITY WARNING: keep the secret key used in production secret!
|
|
477
|
+
SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-change-me-in-production')
|
|
478
|
+
|
|
479
|
+
# SECURITY WARNING: don't run with debug turned on in production!
|
|
480
|
+
DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true'
|
|
481
|
+
|
|
482
|
+
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
|
|
483
|
+
|
|
484
|
+
# Application definition
|
|
485
|
+
INSTALLED_APPS = [
|
|
486
|
+
'django.contrib.admin',
|
|
487
|
+
'django.contrib.auth',
|
|
488
|
+
'django.contrib.contenttypes',
|
|
489
|
+
'django.contrib.sessions',
|
|
490
|
+
'django.contrib.messages',
|
|
491
|
+
'django.contrib.staticfiles',
|
|
492
|
+
{drf_apps}
|
|
493
|
+
# Add your apps here
|
|
494
|
+
]
|
|
495
|
+
|
|
496
|
+
MIDDLEWARE = [
|
|
497
|
+
'django.middleware.security.SecurityMiddleware',
|
|
498
|
+
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
499
|
+
'django.middleware.common.CommonMiddleware',
|
|
500
|
+
'django.middleware.csrf.CsrfViewMiddleware',
|
|
501
|
+
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
502
|
+
'django.contrib.messages.middleware.MessageMiddleware',
|
|
503
|
+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
504
|
+
]
|
|
505
|
+
|
|
506
|
+
ROOT_URLCONF = '{name}.urls'
|
|
507
|
+
|
|
508
|
+
TEMPLATES = [
|
|
509
|
+
{{
|
|
510
|
+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
511
|
+
'DIRS': [BASE_DIR / 'templates'],
|
|
512
|
+
'APP_DIRS': True,
|
|
513
|
+
'OPTIONS': {{
|
|
514
|
+
'context_processors': [
|
|
515
|
+
'django.template.context_processors.debug',
|
|
516
|
+
'django.template.context_processors.request',
|
|
517
|
+
'django.contrib.auth.context_processors.auth',
|
|
518
|
+
'django.contrib.messages.context_processors.messages',
|
|
519
|
+
],
|
|
520
|
+
}},
|
|
521
|
+
}},
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
WSGI_APPLICATION = '{name}.wsgi.application'
|
|
525
|
+
|
|
526
|
+
# Database
|
|
527
|
+
DATABASES = {{
|
|
528
|
+
'default': {{
|
|
529
|
+
'ENGINE': 'django.db.backends.sqlite3',
|
|
530
|
+
'NAME': BASE_DIR / 'db.sqlite3',
|
|
531
|
+
}}
|
|
532
|
+
}}
|
|
533
|
+
|
|
534
|
+
# Password validation
|
|
535
|
+
AUTH_PASSWORD_VALIDATORS = [
|
|
536
|
+
{{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}},
|
|
537
|
+
{{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}},
|
|
538
|
+
{{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}},
|
|
539
|
+
{{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}},
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
# Internationalization
|
|
543
|
+
LANGUAGE_CODE = 'en-us'
|
|
544
|
+
TIME_ZONE = 'UTC'
|
|
545
|
+
USE_I18N = True
|
|
546
|
+
USE_TZ = True
|
|
547
|
+
|
|
548
|
+
# Static files (CSS, JavaScript, Images)
|
|
549
|
+
STATIC_URL = 'static/'
|
|
550
|
+
STATICFILES_DIRS = [BASE_DIR / 'static']
|
|
551
|
+
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
552
|
+
|
|
553
|
+
# Media files
|
|
554
|
+
MEDIA_URL = 'media/'
|
|
555
|
+
MEDIA_ROOT = BASE_DIR / 'media'
|
|
556
|
+
|
|
557
|
+
# Default primary key field type
|
|
558
|
+
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
559
|
+
{drf_settings}
|
|
560
|
+
'''
|
|
561
|
+
|
|
562
|
+
def _generate_urls(self, name: str, with_drf: bool) -> str:
|
|
563
|
+
"""Generate urls.py"""
|
|
564
|
+
drf_imports = "from rest_framework import routers" if with_drf else ""
|
|
565
|
+
drf_urls = '''
|
|
566
|
+
router = routers.DefaultRouter()
|
|
567
|
+
# Register your viewsets here
|
|
568
|
+
# router.register(r'items', ItemViewSet)
|
|
569
|
+
''' if with_drf else ""
|
|
570
|
+
drf_include = "path('api/', include(router.urls))," if with_drf else ""
|
|
571
|
+
|
|
572
|
+
return f'''"""
|
|
573
|
+
URL configuration for {name} project.
|
|
574
|
+
"""
|
|
575
|
+
from django.contrib import admin
|
|
576
|
+
from django.urls import path, include
|
|
577
|
+
{drf_imports}
|
|
578
|
+
{drf_urls}
|
|
579
|
+
urlpatterns = [
|
|
580
|
+
path('admin/', admin.site.urls),
|
|
581
|
+
{drf_include}
|
|
582
|
+
]
|
|
583
|
+
'''
|
|
584
|
+
|
|
585
|
+
def _generate_wsgi(self, name: str) -> str:
|
|
586
|
+
"""Generate wsgi.py"""
|
|
587
|
+
return f'''"""
|
|
588
|
+
WSGI config for {name} project.
|
|
589
|
+
"""
|
|
590
|
+
import os
|
|
591
|
+
from django.core.wsgi import get_wsgi_application
|
|
592
|
+
|
|
593
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{name}.settings')
|
|
594
|
+
application = get_wsgi_application()
|
|
595
|
+
'''
|
|
596
|
+
|
|
597
|
+
def _generate_asgi(self, name: str) -> str:
|
|
598
|
+
"""Generate asgi.py"""
|
|
599
|
+
return f'''"""
|
|
600
|
+
ASGI config for {name} project.
|
|
601
|
+
"""
|
|
602
|
+
import os
|
|
603
|
+
from django.core.asgi import get_asgi_application
|
|
604
|
+
|
|
605
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{name}.settings')
|
|
606
|
+
application = get_asgi_application()
|
|
607
|
+
'''
|
|
608
|
+
|
|
609
|
+
def _generate_requirements(self, with_drf: bool) -> str:
|
|
610
|
+
"""Generate requirements.txt"""
|
|
611
|
+
reqs = [
|
|
612
|
+
"Django>=4.2",
|
|
613
|
+
"python-dotenv>=1.0.0",
|
|
614
|
+
"gunicorn>=21.0.0",
|
|
615
|
+
]
|
|
616
|
+
if with_drf:
|
|
617
|
+
reqs.append("djangorestframework>=3.14.0")
|
|
618
|
+
|
|
619
|
+
reqs.extend([
|
|
620
|
+
"pytest>=7.0.0",
|
|
621
|
+
"pytest-django>=4.5.0",
|
|
622
|
+
])
|
|
623
|
+
|
|
624
|
+
return '\n'.join(reqs) + '\n'
|
|
625
|
+
|
|
626
|
+
def _generate_env_example(self, name: str) -> str:
|
|
627
|
+
"""Generate .env.example"""
|
|
628
|
+
return f'''# {name} Environment Variables
|
|
629
|
+
DEBUG=True
|
|
630
|
+
SECRET_KEY=your-secret-key-change-in-production
|
|
631
|
+
ALLOWED_HOSTS=localhost,127.0.0.1
|
|
632
|
+
DATABASE_URL=sqlite:///db.sqlite3
|
|
633
|
+
'''
|
|
634
|
+
|
|
635
|
+
def _generate_gitignore(self) -> str:
|
|
636
|
+
"""Generate .gitignore"""
|
|
637
|
+
return '''# Python
|
|
638
|
+
__pycache__/
|
|
639
|
+
*.py[cod]
|
|
640
|
+
*$py.class
|
|
641
|
+
*.so
|
|
642
|
+
.Python
|
|
643
|
+
venv/
|
|
644
|
+
.venv/
|
|
645
|
+
ENV/
|
|
646
|
+
|
|
647
|
+
# Django
|
|
648
|
+
*.log
|
|
649
|
+
local_settings.py
|
|
650
|
+
db.sqlite3
|
|
651
|
+
*.pot
|
|
652
|
+
*.pyc
|
|
653
|
+
staticfiles/
|
|
654
|
+
media/
|
|
655
|
+
|
|
656
|
+
# IDE
|
|
657
|
+
.idea/
|
|
658
|
+
.vscode/
|
|
659
|
+
*.swp
|
|
660
|
+
*.swo
|
|
661
|
+
|
|
662
|
+
# Environment
|
|
663
|
+
.env
|
|
664
|
+
.env.local
|
|
665
|
+
|
|
666
|
+
# Testing
|
|
667
|
+
.coverage
|
|
668
|
+
htmlcov/
|
|
669
|
+
.pytest_cache/
|
|
670
|
+
'''
|
|
671
|
+
|
|
672
|
+
def _generate_models_py(self) -> str:
|
|
673
|
+
"""Generate models.py"""
|
|
674
|
+
return '''"""
|
|
675
|
+
Models for this app.
|
|
676
|
+
"""
|
|
677
|
+
from django.db import models
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
# Create your models here.
|
|
681
|
+
class BaseModel(models.Model):
|
|
682
|
+
"""Abstract base model with common fields."""
|
|
683
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
684
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
685
|
+
|
|
686
|
+
class Meta:
|
|
687
|
+
abstract = True
|
|
688
|
+
'''
|
|
689
|
+
|
|
690
|
+
def _generate_views_py(self) -> str:
|
|
691
|
+
"""Generate views.py"""
|
|
692
|
+
return '''"""
|
|
693
|
+
Views for this app.
|
|
694
|
+
"""
|
|
695
|
+
from django.shortcuts import render
|
|
696
|
+
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
# Create your views here.
|
|
700
|
+
'''
|
|
701
|
+
|
|
702
|
+
def _generate_app_urls(self, name: str) -> str:
|
|
703
|
+
"""Generate app urls.py"""
|
|
704
|
+
return f'''"""
|
|
705
|
+
URL patterns for {name} app.
|
|
706
|
+
"""
|
|
707
|
+
from django.urls import path
|
|
708
|
+
from . import views
|
|
709
|
+
|
|
710
|
+
app_name = '{name}'
|
|
711
|
+
|
|
712
|
+
urlpatterns = [
|
|
713
|
+
# Add your URL patterns here
|
|
714
|
+
]
|
|
715
|
+
'''
|
|
716
|
+
|
|
717
|
+
def _generate_admin_py(self) -> str:
|
|
718
|
+
"""Generate admin.py"""
|
|
719
|
+
return '''"""
|
|
720
|
+
Admin configuration for this app.
|
|
721
|
+
"""
|
|
722
|
+
from django.contrib import admin
|
|
723
|
+
|
|
724
|
+
# Register your models here.
|
|
725
|
+
'''
|
|
726
|
+
|
|
727
|
+
def _generate_apps_py(self, name: str) -> str:
|
|
728
|
+
"""Generate apps.py"""
|
|
729
|
+
return f'''"""
|
|
730
|
+
App configuration.
|
|
731
|
+
"""
|
|
732
|
+
from django.apps import AppConfig
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
class {name.title().replace("_", "")}Config(AppConfig):
|
|
736
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
737
|
+
name = 'apps.{name}'
|
|
738
|
+
'''
|
|
739
|
+
|
|
740
|
+
def _generate_serializers_py(self) -> str:
|
|
741
|
+
"""Generate serializers.py"""
|
|
742
|
+
return '''"""
|
|
743
|
+
DRF Serializers for this app.
|
|
744
|
+
"""
|
|
745
|
+
from rest_framework import serializers
|
|
746
|
+
|
|
747
|
+
# Create your serializers here.
|
|
748
|
+
'''
|
|
749
|
+
|
|
750
|
+
def _generate_tests_py(self, name: str) -> str:
|
|
751
|
+
"""Generate tests.py"""
|
|
752
|
+
return f'''"""
|
|
753
|
+
Tests for {name} app.
|
|
754
|
+
"""
|
|
755
|
+
from django.test import TestCase
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
class {name.title().replace("_", "")}Tests(TestCase):
|
|
759
|
+
"""Tests for {name} app."""
|
|
760
|
+
|
|
761
|
+
def test_placeholder(self):
|
|
762
|
+
"""Placeholder test."""
|
|
763
|
+
self.assertTrue(True)
|
|
764
|
+
'''
|
|
765
|
+
|
|
766
|
+
def _generate_model_code(self, name: str, fields: Dict[str, str]) -> str:
|
|
767
|
+
"""Generate model code"""
|
|
768
|
+
field_mappings = {
|
|
769
|
+
"str": "CharField(max_length=255)",
|
|
770
|
+
"string": "CharField(max_length=255)",
|
|
771
|
+
"text": "TextField()",
|
|
772
|
+
"int": "IntegerField()",
|
|
773
|
+
"integer": "IntegerField()",
|
|
774
|
+
"float": "FloatField()",
|
|
775
|
+
"decimal": "DecimalField(max_digits=10, decimal_places=2)",
|
|
776
|
+
"bool": "BooleanField(default=False)",
|
|
777
|
+
"boolean": "BooleanField(default=False)",
|
|
778
|
+
"date": "DateField()",
|
|
779
|
+
"datetime": "DateTimeField()",
|
|
780
|
+
"email": "EmailField()",
|
|
781
|
+
"url": "URLField()",
|
|
782
|
+
"file": "FileField(upload_to='files/')",
|
|
783
|
+
"image": "ImageField(upload_to='images/')",
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
code = f'''class {name}(BaseModel):
|
|
787
|
+
"""{name} model."""
|
|
788
|
+
'''
|
|
789
|
+
for field_name, field_type in fields.items():
|
|
790
|
+
django_field = field_mappings.get(field_type.lower(), f"CharField(max_length=255) # {field_type}")
|
|
791
|
+
code += f' {field_name} = models.{django_field}\n'
|
|
792
|
+
|
|
793
|
+
code += f'''
|
|
794
|
+
class Meta:
|
|
795
|
+
ordering = ['-created_at']
|
|
796
|
+
verbose_name = '{name}'
|
|
797
|
+
verbose_name_plural = '{name}s'
|
|
798
|
+
|
|
799
|
+
def __str__(self):
|
|
800
|
+
return f"{name} {{self.id}}"
|
|
801
|
+
'''
|
|
802
|
+
return code
|
|
803
|
+
|
|
804
|
+
def _generate_function_view(self, name: str) -> str:
|
|
805
|
+
"""Generate function-based view"""
|
|
806
|
+
return f'''def {name.lower()}_view(request):
|
|
807
|
+
"""{name} view."""
|
|
808
|
+
context = {{}}
|
|
809
|
+
return render(request, '{name.lower()}.html', context)
|
|
810
|
+
'''
|
|
811
|
+
|
|
812
|
+
def _generate_class_view(self, name: str) -> str:
|
|
813
|
+
"""Generate class-based view"""
|
|
814
|
+
return f'''class {name}View(ListView):
|
|
815
|
+
"""{name} list view."""
|
|
816
|
+
# model = {name}
|
|
817
|
+
template_name = '{name.lower()}_list.html'
|
|
818
|
+
context_object_name = 'items'
|
|
819
|
+
paginate_by = 10
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
class {name}DetailView(DetailView):
|
|
823
|
+
"""{name} detail view."""
|
|
824
|
+
# model = {name}
|
|
825
|
+
template_name = '{name.lower()}_detail.html'
|
|
826
|
+
context_object_name = 'item'
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
class {name}CreateView(CreateView):
|
|
830
|
+
"""{name} create view."""
|
|
831
|
+
# model = {name}
|
|
832
|
+
# fields = ['field1', 'field2']
|
|
833
|
+
template_name = '{name.lower()}_form.html'
|
|
834
|
+
success_url = '/'
|
|
835
|
+
'''
|
|
836
|
+
|
|
837
|
+
def _generate_viewset(self, name: str) -> str:
|
|
838
|
+
"""Generate DRF ViewSet"""
|
|
839
|
+
return f'''from rest_framework import viewsets
|
|
840
|
+
from rest_framework.permissions import IsAuthenticated
|
|
841
|
+
# from .models import {name}
|
|
842
|
+
# from .serializers import {name}Serializer
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
class {name}ViewSet(viewsets.ModelViewSet):
|
|
846
|
+
"""{name} API ViewSet."""
|
|
847
|
+
# queryset = {name}.objects.all()
|
|
848
|
+
# serializer_class = {name}Serializer
|
|
849
|
+
permission_classes = [IsAuthenticated]
|
|
850
|
+
|
|
851
|
+
def get_queryset(self):
|
|
852
|
+
"""Filter queryset based on user."""
|
|
853
|
+
return super().get_queryset()
|
|
854
|
+
'''
|
|
855
|
+
|
|
856
|
+
def _generate_serializer_code(self, model: str) -> str:
|
|
857
|
+
"""Generate serializer code"""
|
|
858
|
+
return f'''from rest_framework import serializers
|
|
859
|
+
# from .models import {model}
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
class {model}Serializer(serializers.ModelSerializer):
|
|
863
|
+
"""{model} serializer."""
|
|
864
|
+
|
|
865
|
+
class Meta:
|
|
866
|
+
# model = {model}
|
|
867
|
+
fields = '__all__'
|
|
868
|
+
read_only_fields = ['id', 'created_at', 'updated_at']
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
class {model}CreateSerializer(serializers.ModelSerializer):
|
|
872
|
+
"""Serializer for creating {model}."""
|
|
873
|
+
|
|
874
|
+
class Meta:
|
|
875
|
+
# model = {model}
|
|
876
|
+
exclude = ['id', 'created_at', 'updated_at']
|
|
877
|
+
'''
|
|
878
|
+
|
|
879
|
+
def can_handle(self, request: str) -> float:
|
|
880
|
+
"""Check if request is Django-related"""
|
|
881
|
+
request_lower = request.lower()
|
|
882
|
+
|
|
883
|
+
high_conf = ["django", "drf", "django rest", "manage.py"]
|
|
884
|
+
for kw in high_conf:
|
|
885
|
+
if kw in request_lower:
|
|
886
|
+
return 0.9
|
|
887
|
+
|
|
888
|
+
med_conf = ["model", "view", "serializer", "migration", "admin"]
|
|
889
|
+
for kw in med_conf:
|
|
890
|
+
if kw in request_lower:
|
|
891
|
+
return 0.5
|
|
892
|
+
|
|
893
|
+
return super().can_handle(request)
|
|
894
|
+
|
|
895
|
+
def handle_request(self, request: str, **kwargs) -> Optional[ActionResult]:
|
|
896
|
+
"""Handle a natural language request"""
|
|
897
|
+
request_lower = request.lower()
|
|
898
|
+
|
|
899
|
+
if "scaffold" in request_lower or "new project" in request_lower:
|
|
900
|
+
words = request.split()
|
|
901
|
+
name = "myproject"
|
|
902
|
+
for i, word in enumerate(words):
|
|
903
|
+
if word.lower() in ["project", "called", "named"]:
|
|
904
|
+
if i + 1 < len(words):
|
|
905
|
+
name = words[i + 1].strip("'\"")
|
|
906
|
+
break
|
|
907
|
+
return self.scaffold_project(name=name)
|
|
908
|
+
|
|
909
|
+
if "analyze" in request_lower:
|
|
910
|
+
return self.analyze_project()
|
|
911
|
+
|
|
912
|
+
return None
|