django-angular3 0.1.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.
Files changed (54) hide show
  1. django_angular3/__init__.py +5 -0
  2. django_angular3/__main__.py +4 -0
  3. django_angular3/admin.py +4 -0
  4. django_angular3/angular.py +335 -0
  5. django_angular3/apps.py +7 -0
  6. django_angular3/build.py +66 -0
  7. django_angular3/cli.py +313 -0
  8. django_angular3/config.py +110 -0
  9. django_angular3/documents.py +52 -0
  10. django_angular3/examples/01_simple_crm/django-angular3.json +10 -0
  11. django_angular3/examples/01_simple_crm/manage.py +23 -0
  12. django_angular3/examples/01_simple_crm/shop/__init__.py +0 -0
  13. django_angular3/examples/01_simple_crm/shop/admin.py +17 -0
  14. django_angular3/examples/01_simple_crm/shop/apps.py +6 -0
  15. django_angular3/examples/01_simple_crm/shop/models.py +20 -0
  16. django_angular3/examples/01_simple_crm/shop/serializers.py +15 -0
  17. django_angular3/examples/01_simple_crm/shop/tests.py +1 -0
  18. django_angular3/examples/01_simple_crm/shop/views.py +24 -0
  19. django_angular3/examples/01_simple_crm/simple_crm/__init__.py +0 -0
  20. django_angular3/examples/01_simple_crm/simple_crm/asgi.py +7 -0
  21. django_angular3/examples/01_simple_crm/simple_crm/settings.py +107 -0
  22. django_angular3/examples/01_simple_crm/simple_crm/urls.py +14 -0
  23. django_angular3/examples/01_simple_crm/simple_crm/wsgi.py +7 -0
  24. django_angular3/examples/01_simple_crm/ui.json +13 -0
  25. django_angular3/examples/__init__.py +0 -0
  26. django_angular3/management/__init__.py +1 -0
  27. django_angular3/management/commands/__init__.py +1 -0
  28. django_angular3/management/commands/_base.py +57 -0
  29. django_angular3/management/commands/build_app.py +483 -0
  30. django_angular3/management/commands/export_schema.py +106 -0
  31. django_angular3/management/commands/ng_add.py +19 -0
  32. django_angular3/management/commands/ng_build.py +6 -0
  33. django_angular3/management/commands/ng_config.py +6 -0
  34. django_angular3/management/commands/ng_gen_app.py +22 -0
  35. django_angular3/management/commands/ng_new.py +6 -0
  36. django_angular3/management/commands/ng_openapi_gen.py +6 -0
  37. django_angular3/management/commands/ng_workspace.py +6 -0
  38. django_angular3/management/commands/ng_workspace_delete.py +6 -0
  39. django_angular3/management/commands/ng_workspace_modify.py +6 -0
  40. django_angular3/migrations/__init__.py +2 -0
  41. django_angular3/models.py +2 -0
  42. django_angular3/settings.py +123 -0
  43. django_angular3/static/django_angular3/.gitkeep +0 -0
  44. django_angular3/templates/django_angular3/.gitkeep +0 -0
  45. django_angular3/tools.py +134 -0
  46. django_angular3/urls.py +6 -0
  47. django_angular3/validation.py +169 -0
  48. django_angular3/views.py +2 -0
  49. django_angular3-0.1.0.dist-info/METADATA +296 -0
  50. django_angular3-0.1.0.dist-info/RECORD +54 -0
  51. django_angular3-0.1.0.dist-info/WHEEL +5 -0
  52. django_angular3-0.1.0.dist-info/entry_points.txt +2 -0
  53. django_angular3-0.1.0.dist-info/licenses/LICENSE +21 -0
  54. django_angular3-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ """django-angular3 package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0a1"
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,4 @@
1
+ # This Django app does not currently register any admin models.
2
+ # Admin registrations may be added in the future if needed.
3
+
4
+ from django.contrib import admin # noqa: F401
@@ -0,0 +1,335 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from .config import ProjectConfig, load_project_config
11
+ from .settings import AngularCommandError, AngularSettings, load_angular_settings
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AngularInvocation:
16
+ """A single Angular CLI invocation and the directory it should run from."""
17
+
18
+ command_name: str
19
+ argv: tuple[str, ...]
20
+ cwd: Path
21
+
22
+ def to_dict(self) -> dict[str, object]:
23
+ return {"argv": list(self.argv), "cwd": str(self.cwd)}
24
+
25
+
26
+ AngularInvocationBuilder = Callable[..., list[AngularInvocation]]
27
+
28
+
29
+ def resolve_angular_command(
30
+ command_name: str, config_path: str | Path | None = None, **options: Any
31
+ ) -> list[AngularInvocation]:
32
+ """Resolve one logical djng command into an ordered list of subprocess calls.
33
+
34
+ This function does not execute Angular or Node tooling. It loads the
35
+ project configuration and Angular settings, chooses the matching command
36
+ builder, and returns the concrete ``AngularInvocation`` list that a dry run
37
+ can print or ``execute_invocations`` can later run.
38
+ """
39
+ settings = load_angular_settings()
40
+ config = load_project_config(config_path or settings.config_path)
41
+
42
+ builder = _COMMAND_BUILDERS.get(command_name)
43
+ if builder is None:
44
+ raise AngularCommandError(f"Unknown Angular command '{command_name}'.")
45
+
46
+ return builder(config, settings, **options)
47
+
48
+
49
+ def format_invocations(invocations: list[AngularInvocation]) -> str:
50
+ """Serialize resolved subprocess calls for dry-run output."""
51
+ return json.dumps([invocation.to_dict() for invocation in invocations], indent=2)
52
+
53
+
54
+ def execute_invocations(
55
+ invocations: list[AngularInvocation], settings: AngularSettings | None = None
56
+ ) -> None:
57
+ """Run a previously resolved ordered list of subprocess calls."""
58
+ active_settings = settings or load_angular_settings()
59
+ for invocation in invocations:
60
+ _ensure_command_is_allowed(invocation.command_name, active_settings)
61
+ try:
62
+ subprocess.run(invocation.argv, cwd=invocation.cwd, check=True)
63
+ except FileNotFoundError as exc:
64
+ raise AngularCommandError(
65
+ f"Command not found: {invocation.argv[0]}"
66
+ ) from exc
67
+ except subprocess.CalledProcessError as exc:
68
+ raise AngularCommandError(
69
+ f"Command '{' '.join(invocation.argv)}' failed with exit code "
70
+ f"{exc.returncode}."
71
+ ) from exc
72
+
73
+
74
+ def build_ng_new_invocations(
75
+ config: ProjectConfig, settings: AngularSettings, **_: Any
76
+ ) -> list[AngularInvocation]:
77
+ # Ensure the parent directory exists before calling subprocess.run
78
+ config.angular_output.parent.mkdir(parents=True, exist_ok=True)
79
+ return [
80
+ AngularInvocation(
81
+ command_name="ng_new",
82
+ argv=(
83
+ settings.ng_executable,
84
+ "new",
85
+ config.project_name,
86
+ "--defaults",
87
+ "--skip-git",
88
+ "--skip-install",
89
+ "--no-create-application",
90
+ f"--package-manager={settings.package_manager}",
91
+ f"--directory={config.angular_output.name}",
92
+ ),
93
+ cwd=config.angular_output.parent,
94
+ )
95
+ ]
96
+
97
+
98
+ def build_ng_workspace_schematic_invocations(
99
+ config: ProjectConfig, settings: AngularSettings, **_: Any
100
+ ) -> list[AngularInvocation]:
101
+ return [
102
+ AngularInvocation(
103
+ command_name="ng_workspace",
104
+ argv=(
105
+ settings.ng_executable,
106
+ "generate",
107
+ "angular-django2:ng-workspace",
108
+ config.project_name,
109
+ ),
110
+ cwd=config.angular_output,
111
+ )
112
+ ]
113
+
114
+
115
+ def build_ng_config_invocations(
116
+ config: ProjectConfig, settings: AngularSettings, **_: Any
117
+ ) -> list[AngularInvocation]:
118
+ return [
119
+ AngularInvocation(
120
+ command_name="ng_config",
121
+ argv=(
122
+ settings.ng_executable,
123
+ "config",
124
+ "cli.packageManager",
125
+ settings.package_manager,
126
+ ),
127
+ cwd=config.angular_output,
128
+ ),
129
+ AngularInvocation(
130
+ command_name="ng_config",
131
+ argv=(
132
+ settings.ng_executable,
133
+ "config",
134
+ "schematics.@schematics/angular:application.style",
135
+ settings.style,
136
+ ),
137
+ cwd=config.angular_output,
138
+ ),
139
+ AngularInvocation(
140
+ command_name="ng_config",
141
+ argv=(
142
+ settings.ng_executable,
143
+ "config",
144
+ "schematics.@schematics/angular:application.routing",
145
+ _stringify_bool(settings.routing),
146
+ ),
147
+ cwd=config.angular_output,
148
+ ),
149
+ ]
150
+
151
+
152
+ def build_ng_build_invocations(
153
+ config: ProjectConfig, settings: AngularSettings, **_: Any
154
+ ) -> list[AngularInvocation]:
155
+ return [
156
+ AngularInvocation(
157
+ command_name="ng_build",
158
+ argv=(
159
+ settings.ng_executable,
160
+ "build",
161
+ config.project_name,
162
+ f"--configuration={settings.build_configuration}",
163
+ ),
164
+ cwd=config.angular_output,
165
+ )
166
+ ]
167
+
168
+
169
+ def build_ng_gen_app_invocations(
170
+ config: ProjectConfig,
171
+ settings: AngularSettings,
172
+ *,
173
+ app_name: str | None = None,
174
+ **_: Any,
175
+ ) -> list[AngularInvocation]:
176
+ target_app = app_name or config.project_name
177
+ return [
178
+ AngularInvocation(
179
+ command_name="ng_gen_app",
180
+ argv=(
181
+ settings.ng_executable,
182
+ "generate",
183
+ "angular-django2:ng-app",
184
+ target_app,
185
+ f"--style={settings.style}",
186
+ "--routing" if settings.routing else "--no-routing",
187
+ ),
188
+ cwd=config.angular_output,
189
+ )
190
+ ]
191
+
192
+
193
+ def build_ng_openapi_gen_invocations(
194
+ config: ProjectConfig, settings: AngularSettings, **_: Any
195
+ ) -> list[AngularInvocation]:
196
+ source = config.ng_openapi_gen_config or config.openapi_source
197
+ source_flag = "-c" if config.ng_openapi_gen_config else "-i"
198
+ return [
199
+ AngularInvocation(
200
+ command_name="ng_openapi_gen",
201
+ argv=(
202
+ settings.pnpm_executable,
203
+ "exec",
204
+ "ng-openapi-gen",
205
+ source_flag,
206
+ str(source),
207
+ ),
208
+ cwd=config.angular_output,
209
+ )
210
+ ]
211
+
212
+
213
+ def build_ng_add_invocations(
214
+ config: ProjectConfig,
215
+ settings: AngularSettings,
216
+ *,
217
+ package: str | None = None,
218
+ **_: Any,
219
+ ) -> list[AngularInvocation]:
220
+ target_package = package or settings.ng_add_package
221
+ return [
222
+ AngularInvocation(
223
+ command_name="ng_add",
224
+ argv=(
225
+ settings.ng_executable,
226
+ "add",
227
+ target_package,
228
+ "--skip-confirmation",
229
+ ),
230
+ cwd=config.angular_output,
231
+ )
232
+ ]
233
+
234
+
235
+ def build_ng_workspace_invocations(
236
+ config: ProjectConfig, settings: AngularSettings, **_: Any
237
+ ) -> list[AngularInvocation]:
238
+ """Create and bootstrap an Angular workspace with angular-django2 defaults."""
239
+ invocations = _relabel_invocations(
240
+ build_ng_new_invocations(config, settings), "ng_workspace"
241
+ )
242
+ invocations.extend(
243
+ _relabel_invocations(
244
+ build_ng_config_invocations(config, settings), "ng_workspace"
245
+ )
246
+ )
247
+ invocations.extend(
248
+ _relabel_invocations(build_ng_add_invocations(config, settings), "ng_workspace")
249
+ )
250
+ invocations.extend(build_ng_workspace_schematic_invocations(config, settings))
251
+ return invocations
252
+
253
+
254
+ def build_ng_workspace_modify_invocations(
255
+ config: ProjectConfig, settings: AngularSettings, **_: Any
256
+ ) -> list[AngularInvocation]:
257
+ """Reapply workspace defaults, collection registration, and
258
+ workspace scaffolding."""
259
+ invocations = _relabel_invocations(
260
+ build_ng_config_invocations(config, settings), "ng_workspace_modify"
261
+ )
262
+ invocations.extend(
263
+ _relabel_invocations(
264
+ build_ng_add_invocations(config, settings), "ng_workspace_modify"
265
+ )
266
+ )
267
+ invocations.extend(
268
+ _relabel_invocations(
269
+ build_ng_workspace_schematic_invocations(config, settings),
270
+ "ng_workspace_modify",
271
+ )
272
+ )
273
+ return invocations
274
+
275
+
276
+ def build_ng_workspace_delete_invocations(
277
+ config: ProjectConfig, _settings: AngularSettings, **_: Any
278
+ ) -> list[AngularInvocation]:
279
+ """Delete the entire workspace folder using Python's native
280
+ cross-platform shutil."""
281
+ import sys
282
+
283
+ py_code = (
284
+ f"import shutil; shutil.rmtree(r'{config.angular_output}', ignore_errors=True)"
285
+ )
286
+
287
+ return [
288
+ AngularInvocation(
289
+ command_name="ng_workspace_delete",
290
+ argv=(sys.executable, "-c", py_code),
291
+ cwd=config.angular_output.parent,
292
+ )
293
+ ]
294
+
295
+
296
+ _COMMAND_BUILDERS: dict[str, AngularInvocationBuilder] = {
297
+ "ng_new": build_ng_new_invocations,
298
+ "ng_workspace": build_ng_workspace_invocations,
299
+ "ng_config": build_ng_config_invocations,
300
+ "ng_build": build_ng_build_invocations,
301
+ "ng_gen_app": build_ng_gen_app_invocations,
302
+ "ng_openapi_gen": build_ng_openapi_gen_invocations,
303
+ "ng_add": build_ng_add_invocations,
304
+ "ng_workspace_modify": build_ng_workspace_modify_invocations,
305
+ "ng_workspace_delete": build_ng_workspace_delete_invocations,
306
+ }
307
+
308
+
309
+ def _stringify_bool(value: bool) -> str:
310
+ return "true" if value else "false"
311
+
312
+
313
+ def _relabel_invocations(
314
+ invocations: list[AngularInvocation], command_name: str
315
+ ) -> list[AngularInvocation]:
316
+ return [
317
+ AngularInvocation(
318
+ command_name=command_name,
319
+ argv=invocation.argv,
320
+ cwd=invocation.cwd,
321
+ )
322
+ for invocation in invocations
323
+ ]
324
+
325
+
326
+ def _ensure_command_is_allowed(command_name: str, settings: AngularSettings) -> None:
327
+ normalized_command_name = command_name.strip().lower()
328
+ if normalized_command_name in settings.command_allowlist:
329
+ return
330
+
331
+ allowed_commands = ", ".join(settings.command_allowlist) or "<none>"
332
+ raise AngularCommandError(
333
+ f"Command '{command_name}' is not allowed. Allowed commands: "
334
+ f"{allowed_commands}."
335
+ )
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoAngular3Config(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "django_angular3"
7
+ verbose_name = "django-angular3"
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import asdict, dataclass
5
+ from pathlib import Path
6
+
7
+ from .config import ProjectConfig
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class BuildTask:
12
+ name: str
13
+ input: str
14
+ output: str
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class BuildPlan:
19
+ project_name: str
20
+ config_path: str
21
+ tasks: list[BuildTask]
22
+
23
+ def to_dict(self) -> dict[str, object]:
24
+ return {
25
+ "projectName": self.project_name,
26
+ "configPath": self.config_path,
27
+ "tasks": [asdict(task) for task in self.tasks],
28
+ }
29
+
30
+
31
+ def create_build_plan(config: ProjectConfig) -> BuildPlan:
32
+ tasks = [
33
+ BuildTask(
34
+ name="validate-openapi",
35
+ input=str(config.openapi_source),
36
+ output="validated OpenAPI document",
37
+ ),
38
+ BuildTask(
39
+ name="generate-openapi-clients",
40
+ input=str(config.openapi_generator_config or config.openapi_source),
41
+ output=str(config.angular_output / "generated"),
42
+ ),
43
+ BuildTask(
44
+ name="validate-ui",
45
+ input=str(config.ui_source),
46
+ output="validated UI definition document",
47
+ ),
48
+ BuildTask(
49
+ name="assemble-angular-application",
50
+ input=str(config.ui_source),
51
+ output=str(config.angular_output),
52
+ ),
53
+ ]
54
+ return BuildPlan(
55
+ project_name=config.project_name,
56
+ config_path=str(config.config_path),
57
+ tasks=tasks,
58
+ )
59
+
60
+
61
+ def write_build_plan(plan: BuildPlan, output_dir: str | Path) -> Path:
62
+ output_path = Path(output_dir)
63
+ output_path.mkdir(parents=True, exist_ok=True)
64
+ plan_path = output_path / "plan.json"
65
+ plan_path.write_text(json.dumps(plan.to_dict(), indent=2), encoding="utf-8")
66
+ return plan_path