django-cfg 1.3.9__py3-none-any.whl → 1.3.13__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 (188) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/inlines.py +11 -5
  3. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  4. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  5. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  14. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  15. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  16. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  17. django_cfg/apps/payments/config/__init__.py +14 -15
  18. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  19. django_cfg/apps/payments/config/helpers.py +8 -13
  20. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  21. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  22. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  23. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  24. django_cfg/apps/payments/models/payments.py +94 -0
  25. django_cfg/apps/payments/services/core/base.py +4 -4
  26. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  27. django_cfg/apps/payments/services/providers/base.py +209 -3
  28. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  29. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  31. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  32. django_cfg/apps/payments/services/providers/registry.py +5 -5
  33. django_cfg/apps/payments/services/types/requests.py +19 -7
  34. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  35. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  36. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  37. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  38. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  39. django_cfg/apps/payments/urls.py +3 -2
  40. django_cfg/apps/payments/views/api/currencies.py +3 -0
  41. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  42. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  43. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  44. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  45. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  47. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  52. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  53. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  54. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  55. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  56. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  57. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  58. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  59. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  60. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  61. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  62. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  63. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  66. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  67. django_cfg/apps/tasks/urls.py +2 -2
  68. django_cfg/apps/tasks/urls_admin.py +2 -2
  69. django_cfg/apps/tasks/utils/__init__.py +1 -0
  70. django_cfg/apps/tasks/utils/simulator.py +356 -0
  71. django_cfg/apps/tasks/views/__init__.py +16 -0
  72. django_cfg/apps/tasks/views/api.py +569 -0
  73. django_cfg/apps/tasks/views/dashboard.py +58 -0
  74. django_cfg/core/integration/__init__.py +21 -0
  75. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  76. django_cfg/models/constance.py +0 -11
  77. django_cfg/models/payments.py +137 -3
  78. django_cfg/modules/django_tasks.py +54 -21
  79. django_cfg/registry/core.py +4 -9
  80. django_cfg/template_archive/django_sample.zip +0 -0
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/METADATA +2 -2
  82. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/RECORD +85 -153
  83. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  84. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  85. django_cfg/apps/payments/config/constance/fields.py +0 -69
  86. django_cfg/apps/payments/config/constance/settings.py +0 -160
  87. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  88. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  89. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  90. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  91. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  92. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  93. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  94. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  95. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  96. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  97. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  98. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  99. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  100. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  101. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  102. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  103. django_cfg/apps/tasks/views.py +0 -461
  104. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  105. django_cfg/management/commands/app_agent_generate.py +0 -342
  106. django_cfg/management/commands/app_agent_info.py +0 -308
  107. django_cfg/management/commands/auto_generate.py +0 -486
  108. django_cfg/modules/django_app_agent/__init__.py +0 -87
  109. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  110. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  111. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  112. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  113. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  114. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  119. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  120. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  121. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  122. django_cfg/modules/django_app_agent/core/config.py +0 -300
  123. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  124. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  125. django_cfg/modules/django_app_agent/models/base.py +0 -283
  126. django_cfg/modules/django_app_agent/models/context.py +0 -496
  127. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  128. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  129. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  130. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  131. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  132. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  133. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  134. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  135. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  136. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  137. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  138. django_cfg/modules/django_app_agent/services/base.py +0 -437
  139. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  140. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  141. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  142. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  143. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  144. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  145. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  146. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  147. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  148. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  149. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  150. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  151. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  152. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  153. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  154. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  155. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  156. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  157. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  158. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  159. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  160. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  161. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  171. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  172. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  173. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  174. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  175. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  176. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  177. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  178. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  179. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  180. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  181. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  182. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  183. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  184. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  185. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/WHEEL +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/entry_points.txt +0 -0
  188. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,236 +0,0 @@
1
- """
2
- Jinja2 Template Engine for Django App Agent.
3
-
4
- This module provides a powerful Jinja2-based template engine
5
- with custom filters and functions for Django code generation.
6
- """
7
-
8
- from typing import Dict, Any, Set, Optional, List
9
- import re
10
- from pathlib import Path
11
-
12
- from jinja2 import Environment, BaseLoader, TemplateNotFound, select_autoescape
13
- from jinja2.exceptions import TemplateError
14
-
15
- from ...core.exceptions import ValidationError
16
-
17
-
18
- class StringTemplateLoader(BaseLoader):
19
- """Custom Jinja2 loader for string-based templates."""
20
-
21
- def __init__(self, templates: Dict[str, str]):
22
- """Initialize with template dictionary."""
23
- self.templates = templates
24
-
25
- def get_source(self, environment: Environment, template: str) -> tuple:
26
- """Get template source."""
27
- if template not in self.templates:
28
- raise TemplateNotFound(template)
29
-
30
- source = self.templates[template]
31
- return source, None, lambda: True
32
-
33
-
34
- class JinjaTemplateEngine:
35
- """Jinja2-based template engine with Django-specific features."""
36
-
37
- def __init__(self):
38
- """Initialize Jinja2 environment with custom filters."""
39
- self.templates: Dict[str, str] = {}
40
- self.env = self._create_environment()
41
-
42
- def _create_environment(self) -> Environment:
43
- """Create Jinja2 environment with custom filters and functions."""
44
- env = Environment(
45
- loader=StringTemplateLoader(self.templates),
46
- autoescape=select_autoescape(['html', 'xml']),
47
- trim_blocks=True,
48
- lstrip_blocks=True
49
- )
50
-
51
- # Add custom filters
52
- env.filters.update({
53
- 'snake_case': self._snake_case,
54
- 'camel_case': self._camel_case,
55
- 'pascal_case': self._pascal_case,
56
- 'kebab_case': self._kebab_case,
57
- 'title_case': self._title_case,
58
- 'plural': self._pluralize,
59
- 'singular': self._singularize,
60
- 'django_field': self._django_field_repr,
61
- 'python_repr': self._python_repr,
62
- 'indent': self._indent,
63
- 'comment_block': self._comment_block
64
- })
65
-
66
- # Add custom functions
67
- env.globals.update({
68
- 'now': self._now,
69
- 'uuid4': self._uuid4,
70
- 'range': range,
71
- 'enumerate': enumerate,
72
- 'zip': zip
73
- })
74
-
75
- return env
76
-
77
- def add_template(self, name: str, content: str) -> None:
78
- """Add a template to the engine."""
79
- self.templates[name] = content
80
- # Recreate environment to update loader
81
- self.env = self._create_environment()
82
-
83
- def add_templates(self, templates: Dict[str, str]) -> None:
84
- """Add multiple templates to the engine."""
85
- self.templates.update(templates)
86
- # Recreate environment to update loader
87
- self.env = self._create_environment()
88
-
89
- def render(
90
- self,
91
- template_name: str,
92
- variables: Dict[str, Any]
93
- ) -> tuple[str, Set[str], Set[str]]:
94
- """
95
- Render template with variables.
96
-
97
- Returns:
98
- tuple: (rendered_content, variables_used, missing_variables)
99
- """
100
- try:
101
- template = self.env.get_template(template_name)
102
-
103
- # Get template variables (simplified approach - read template source from file)
104
- template_vars = set()
105
- try:
106
- # Get template source from the loader
107
- source, _ = self.env.loader.get_source(self.env, template_name)
108
- ast = self.env.parse(source)
109
- for node in ast.find_all('Name'):
110
- if node.ctx == 'load':
111
- template_vars.add(node.name)
112
- except Exception:
113
- # Fallback: assume all provided variables are used
114
- template_vars = set(variables.keys())
115
-
116
- # Remove Jinja2 built-ins and our custom functions
117
- builtin_vars = {
118
- 'range', 'enumerate', 'zip', 'now', 'uuid4',
119
- 'loop', 'super', 'self', 'varargs', 'kwargs'
120
- }
121
- template_vars = template_vars - builtin_vars
122
-
123
- # Check for missing variables
124
- missing_vars = template_vars - set(variables.keys())
125
- variables_used = template_vars - missing_vars
126
-
127
- # Render template
128
- rendered = template.render(**variables)
129
-
130
- return rendered, variables_used, missing_vars
131
-
132
- except TemplateNotFound:
133
- raise ValidationError(
134
- f"Template '{template_name}' not found",
135
- validation_type="template_not_found"
136
- )
137
- except TemplateError as e:
138
- raise ValidationError(
139
- f"Template rendering failed: {e}",
140
- validation_type="template_rendering"
141
- )
142
-
143
- # Custom filters
144
- def _snake_case(self, text: str) -> str:
145
- """Convert text to snake_case."""
146
- # Handle camelCase and PascalCase
147
- s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', text)
148
- s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1)
149
- return s2.lower().replace(' ', '_').replace('-', '_')
150
-
151
- def _camel_case(self, text: str) -> str:
152
- """Convert text to camelCase."""
153
- components = re.split(r'[_\s-]+', text.lower())
154
- return components[0] + ''.join(word.capitalize() for word in components[1:])
155
-
156
- def _pascal_case(self, text: str) -> str:
157
- """Convert text to PascalCase."""
158
- components = re.split(r'[_\s-]+', text.lower())
159
- return ''.join(word.capitalize() for word in components)
160
-
161
- def _kebab_case(self, text: str) -> str:
162
- """Convert text to kebab-case."""
163
- return self._snake_case(text).replace('_', '-')
164
-
165
- def _title_case(self, text: str) -> str:
166
- """Convert text to Title Case."""
167
- return text.replace('_', ' ').replace('-', ' ').title()
168
-
169
- def _pluralize(self, text: str) -> str:
170
- """Simple pluralization."""
171
- if text.endswith('y'):
172
- return text[:-1] + 'ies'
173
- elif text.endswith(('s', 'sh', 'ch', 'x', 'z')):
174
- return text + 'es'
175
- else:
176
- return text + 's'
177
-
178
- def _singularize(self, text: str) -> str:
179
- """Simple singularization."""
180
- if text.endswith('ies'):
181
- return text[:-3] + 'y'
182
- elif text.endswith('es'):
183
- return text[:-2]
184
- elif text.endswith('s') and not text.endswith('ss'):
185
- return text[:-1]
186
- else:
187
- return text
188
-
189
- def _django_field_repr(self, field_type: str, **kwargs) -> str:
190
- """Generate Django field representation."""
191
- args = []
192
- for key, value in kwargs.items():
193
- if isinstance(value, str):
194
- args.append(f"{key}='{value}'")
195
- else:
196
- args.append(f"{key}={value}")
197
-
198
- if args:
199
- return f"models.{field_type}({', '.join(args)})"
200
- else:
201
- return f"models.{field_type}()"
202
-
203
- def _python_repr(self, value: Any) -> str:
204
- """Python representation of value."""
205
- return repr(value)
206
-
207
- def _indent(self, text: str, width: int = 4, first: bool = False) -> str:
208
- """Indent text by specified width."""
209
- lines = text.split('\n')
210
- indent_str = ' ' * width
211
-
212
- if first:
213
- return '\n'.join(indent_str + line for line in lines)
214
- else:
215
- return lines[0] + '\n' + '\n'.join(indent_str + line for line in lines[1:])
216
-
217
- def _comment_block(self, text: str, style: str = 'python') -> str:
218
- """Create comment block in specified style."""
219
- if style == 'python':
220
- lines = text.split('\n')
221
- return '\n'.join(f'# {line}' for line in lines)
222
- elif style == 'docstring':
223
- return f'"""\n{text}\n"""'
224
- else:
225
- return text
226
-
227
- # Custom functions
228
- def _now(self) -> str:
229
- """Current timestamp."""
230
- from datetime import datetime
231
- return datetime.now().isoformat()
232
-
233
- def _uuid4(self) -> str:
234
- """Generate UUID4."""
235
- import uuid
236
- return str(uuid.uuid4())
@@ -1,159 +0,0 @@
1
- """
2
- Template Manager Service for Django App Agent Module.
3
-
4
- This service provides sophisticated Jinja2-based template rendering
5
- with feature-driven code generation capabilities.
6
- """
7
-
8
- from typing import Dict, Any, Set, Optional, List
9
- from pathlib import Path
10
-
11
- from pydantic import BaseModel, Field
12
-
13
- from ...core.config import AgentConfig
14
- from ...core.exceptions import ValidationError
15
- from ...models.enums import AppType, AppFeature
16
- from ...models.requests import TemplateRequest
17
- from ...models.responses import TemplateResult
18
- from ..base import BaseService, ServiceDependencies
19
- from .jinja_engine import JinjaTemplateEngine
20
- from .template_loader import TemplateLoader
21
- from .variable_processor import VariableProcessor
22
-
23
-
24
- class TemplateManagerService(BaseService[TemplateRequest, TemplateResult]):
25
- """
26
- Advanced template management service with Jinja2 support.
27
-
28
- Features:
29
- - Jinja2 template engine with custom filters
30
- - File-based template loading
31
- - Feature-driven template selection
32
- - Variable processing and validation
33
- - Template caching and optimization
34
- """
35
-
36
- def __init__(self, config: AgentConfig):
37
- """Initialize template manager service."""
38
- super().__init__("template_manager", config)
39
- self.config = config
40
-
41
- # Initialize components
42
- self.template_loader = TemplateLoader()
43
- self.jinja_engine = JinjaTemplateEngine()
44
- self.variable_processor = VariableProcessor()
45
-
46
- # Load all templates into engine
47
- self._load_templates()
48
-
49
- async def process(
50
- self,
51
- request: TemplateRequest,
52
- dependencies: ServiceDependencies
53
- ) -> TemplateResult:
54
- """
55
- Process template rendering request.
56
-
57
- Args:
58
- request: Template processing request
59
- dependencies: Service dependencies
60
-
61
- Returns:
62
- TemplateResult with rendered content
63
- """
64
- dependencies.log_operation(
65
- f"Processing template '{request.template_name}'",
66
- app_type=request.app_type if isinstance(request.app_type, str) else request.app_type.value,
67
- features_count=len(request.features),
68
- variables_count=len(request.variables)
69
- )
70
-
71
- try:
72
- # Determine template name
73
- template_name = self._resolve_template_name(request, dependencies)
74
-
75
- # Process variables
76
- processed_variables = await self.variable_processor.process_variables(
77
- request, dependencies
78
- )
79
-
80
- # Render template
81
- rendered_content, variables_used, missing_vars = self.jinja_engine.render(
82
- template_name, processed_variables
83
- )
84
-
85
- # Create result
86
- result = TemplateResult(
87
- rendered_content=rendered_content,
88
- template_name=template_name,
89
- variables_used=list(variables_used),
90
- missing_variables=list(missing_vars),
91
- template_metadata={
92
- "app_type": request.app_type if isinstance(request.app_type, str) else request.app_type.value,
93
- "features": [(f.value if hasattr(f, 'value') else f) for f in request.features],
94
- "template_engine": "jinja2",
95
- "rendered_size": len(rendered_content),
96
- "variables_processed": len(processed_variables)
97
- }
98
- )
99
-
100
- dependencies.log_operation(
101
- "Template processing completed successfully",
102
- template_name=template_name,
103
- rendered_size=len(rendered_content),
104
- variables_used=len(variables_used),
105
- missing_variables=len(missing_vars)
106
- )
107
-
108
- return result
109
-
110
- except Exception as e:
111
- dependencies.log_error("Template processing failed", e)
112
- raise
113
-
114
- def _load_templates(self) -> None:
115
- """Load all templates into the Jinja2 engine."""
116
- templates = self.template_loader.load_all_templates()
117
- self.jinja_engine.add_templates(templates)
118
-
119
- def _resolve_template_name(
120
- self,
121
- request: TemplateRequest,
122
- dependencies: ServiceDependencies
123
- ) -> str:
124
- """Resolve the actual template name to use."""
125
- template_name = request.template_name
126
-
127
- # If template_name doesn't end with .j2, add it
128
- if not template_name.endswith('.j2'):
129
- template_name = f"{template_name}.j2"
130
-
131
- # Check if template exists
132
- available_templates = self.template_loader.get_available_templates()
133
-
134
- if template_name not in available_templates:
135
- # Try app-type specific template
136
- app_type_str = request.app_type if isinstance(request.app_type, str) else request.app_type.value
137
- app_specific_name = f"{app_type_str}_{template_name}"
138
- if app_specific_name in available_templates:
139
- template_name = app_specific_name
140
- else:
141
- raise ValidationError(
142
- f"Template '{template_name}' not found. Available templates: {available_templates}",
143
- validation_type="template_not_found"
144
- )
145
-
146
- return template_name
147
-
148
- def reload_templates(self) -> None:
149
- """Reload all templates from disk."""
150
- self.template_loader.clear_cache()
151
- self._load_templates()
152
-
153
- def get_available_templates(self) -> List[str]:
154
- """Get list of available template names."""
155
- return self.template_loader.get_available_templates()
156
-
157
- def add_custom_template(self, name: str, content: str) -> None:
158
- """Add a custom template at runtime."""
159
- self.jinja_engine.add_template(name, content)
@@ -1,36 +0,0 @@
1
- """
2
- Data Models for Template Manager Service.
3
-
4
- This module contains Pydantic models for template processing
5
- requests, results, and related data structures.
6
- """
7
-
8
- from typing import Dict, Any, Optional, List, Set
9
-
10
- from pydantic import BaseModel, Field, ConfigDict
11
-
12
- from ...models.enums import AppType, AppFeature
13
-
14
-
15
- class TemplateRequest(BaseModel):
16
- """Request for template processing."""
17
-
18
- model_config = ConfigDict(extra='forbid', validate_assignment=True)
19
-
20
- template_name: str = Field(description="Name of the template to process")
21
- app_type: AppType = Field(description="Type of application")
22
- variables: Dict[str, Any] = Field(default_factory=dict, description="Template variables")
23
- features: List[AppFeature] = Field(default_factory=list, description="Requested features")
24
- custom_templates: Dict[str, str] = Field(default_factory=dict, description="Custom template overrides")
25
-
26
-
27
- class TemplateResult(BaseModel):
28
- """Result of template processing."""
29
-
30
- model_config = ConfigDict(extra='forbid', validate_assignment=True)
31
-
32
- rendered_content: str = Field(description="Rendered template content")
33
- template_name: str = Field(description="Name of the processed template")
34
- variables_used: Set[str] = Field(default_factory=set, description="Variables that were used in rendering")
35
- missing_variables: Set[str] = Field(default_factory=set, description="Variables that were missing")
36
- template_metadata: Dict[str, Any] = Field(default_factory=dict, description="Template processing metadata")
@@ -1,100 +0,0 @@
1
- """
2
- Template Loader for Django App Agent.
3
-
4
- This module handles loading Jinja2 templates from files and
5
- providing them to the template engine.
6
- """
7
-
8
- from typing import Dict, Optional
9
- from pathlib import Path
10
-
11
- from pydantic import BaseModel, Field
12
-
13
- from ...core.exceptions import ValidationError
14
-
15
-
16
- class TemplateLoader(BaseModel):
17
- """Loads Jinja2 templates from the templates directory."""
18
-
19
- templates_dir: Path = Field(description="Directory containing template files")
20
- template_cache: Dict[str, str] = Field(default_factory=dict, description="Template cache")
21
-
22
- def __init__(self, **data):
23
- """Initialize template loader."""
24
- if 'templates_dir' not in data:
25
- # Default to templates directory relative to this file
26
- current_dir = Path(__file__).parent
27
- data['templates_dir'] = current_dir / "templates"
28
-
29
- super().__init__(**data)
30
-
31
- # Validate templates directory exists
32
- if not self.templates_dir.exists():
33
- raise ValidationError(
34
- f"Templates directory does not exist: {self.templates_dir}",
35
- validation_type="template_directory"
36
- )
37
-
38
- def load_template(self, template_name: str) -> str:
39
- """Load a template by name."""
40
- # Check cache first
41
- if template_name in self.template_cache:
42
- return self.template_cache[template_name]
43
-
44
- # Try to load from file
45
- template_path = self.templates_dir / template_name
46
-
47
- if not template_path.exists():
48
- # Try with .j2 extension if not provided
49
- if not template_name.endswith('.j2'):
50
- template_path = self.templates_dir / f"{template_name}.j2"
51
-
52
- if not template_path.exists():
53
- raise ValidationError(
54
- f"Template file not found: {template_name}",
55
- validation_type="template_not_found"
56
- )
57
-
58
- try:
59
- content = template_path.read_text(encoding='utf-8')
60
- # Cache the template
61
- self.template_cache[template_name] = content
62
- return content
63
-
64
- except Exception as e:
65
- raise ValidationError(
66
- f"Failed to read template file {template_name}: {e}",
67
- validation_type="template_read_error"
68
- )
69
-
70
- def load_all_templates(self) -> Dict[str, str]:
71
- """Load all templates from the templates directory."""
72
- templates = {}
73
-
74
- for template_path in self.templates_dir.glob("*.j2"):
75
- template_name = template_path.name
76
- try:
77
- content = self.load_template(template_name)
78
- templates[template_name] = content
79
- except ValidationError:
80
- # Skip templates that can't be loaded
81
- continue
82
-
83
- return templates
84
-
85
- def get_available_templates(self) -> list[str]:
86
- """Get list of available template names."""
87
- return [p.name for p in self.templates_dir.glob("*.j2")]
88
-
89
- def clear_cache(self) -> None:
90
- """Clear the template cache."""
91
- self.template_cache.clear()
92
-
93
- def reload_template(self, template_name: str) -> str:
94
- """Reload a template, bypassing cache."""
95
- # Remove from cache if present
96
- if template_name in self.template_cache:
97
- del self.template_cache[template_name]
98
-
99
- # Load fresh copy
100
- return self.load_template(template_name)
@@ -1,105 +0,0 @@
1
- """
2
- Admin configuration for {{ app_name }} application.
3
-
4
- {{ description }}
5
- """
6
-
7
- from django.contrib import admin
8
- {% if features.advanced_admin %}
9
- from django.utils.html import format_html
10
- from django.urls import reverse
11
- from django.utils.safestring import mark_safe
12
- {% endif %}
13
- from .models import {{ app_name|pascal_case }}Model
14
- {% if features.related_models %}
15
- from .models import {{ app_name|pascal_case }}Category, {{ app_name|pascal_case }}Tag
16
- {% endif %}
17
-
18
-
19
- @admin.register({{ app_name|pascal_case }}Model)
20
- class {{ app_name|pascal_case }}Admin(admin.ModelAdmin):
21
- """Admin interface for {{ app_name }} model."""
22
-
23
- list_display = [
24
- 'name',
25
- {% if features.status_tracking %}'status', {% endif %}
26
- {% if features.user_relations %}'owner', {% endif %}
27
- 'created_at',
28
- 'updated_at'
29
- ]
30
-
31
- list_filter = [
32
- {% if features.status_tracking %}'status', {% endif %}
33
- 'created_at',
34
- {% if features.user_relations %}'owner', {% endif %}
35
- ]
36
-
37
- search_fields = ['name', 'description']
38
-
39
- {% if features.status_tracking %}
40
- list_editable = ['status']
41
- {% endif %}
42
-
43
- readonly_fields = ['created_at', 'updated_at']
44
-
45
- {% if features.advanced_admin %}
46
- fieldsets = (
47
- ('Basic Information', {
48
- 'fields': ('name', 'description')
49
- }),
50
- {% if features.status_tracking %}
51
- ('Status', {
52
- 'fields': ('status',)
53
- }),
54
- {% endif %}
55
- {% if features.user_relations %}
56
- ('Ownership', {
57
- 'fields': ('owner',)
58
- }),
59
- {% endif %}
60
- ('Timestamps', {
61
- 'fields': ('created_at', 'updated_at'),
62
- 'classes': ('collapse',)
63
- }),
64
- )
65
- {% endif %}
66
-
67
- {% if features.user_relations %}
68
- def save_model(self, request, obj, form, change):
69
- if not change: # Creating new object
70
- obj.owner = request.user
71
- super().save_model(request, obj, form, change)
72
-
73
- def get_queryset(self, request):
74
- qs = super().get_queryset(request)
75
- if request.user.is_superuser:
76
- return qs
77
- return qs.filter(owner=request.user)
78
- {% endif %}
79
-
80
- {% if features.advanced_admin %}
81
- def get_readonly_fields(self, request, obj=None):
82
- if obj: # Editing existing object
83
- return self.readonly_fields + ['name']
84
- return self.readonly_fields
85
- {% endif %}
86
-
87
-
88
- {% if features.related_models %}
89
- @admin.register({{ app_name|pascal_case }}Category)
90
- class {{ app_name|pascal_case }}CategoryAdmin(admin.ModelAdmin):
91
- """Admin interface for {{ app_name }} categories."""
92
-
93
- list_display = ['name', 'slug', 'description']
94
- search_fields = ['name', 'description']
95
- prepopulated_fields = {'slug': ('name',)}
96
-
97
-
98
- @admin.register({{ app_name|pascal_case }}Tag)
99
- class {{ app_name|pascal_case }}TagAdmin(admin.ModelAdmin):
100
- """Admin interface for {{ app_name }} tags."""
101
-
102
- list_display = ['name', 'color']
103
- search_fields = ['name']
104
- list_editable = ['color']
105
- {% endif %}
@@ -1,31 +0,0 @@
1
- """
2
- App configuration for {{ app_name }}.
3
-
4
- {{ description }}
5
- """
6
-
7
- from django.apps import AppConfig
8
- {% if app_type == 'django_cfg' %}
9
- from django_cfg.apps import BaseCfgAppConfig
10
- {% endif %}
11
-
12
-
13
- {% if app_type == 'django_cfg' %}
14
- class {{ app_name|pascal_case }}Config(BaseCfgAppConfig):
15
- {% else %}
16
- class {{ app_name|pascal_case }}Config(AppConfig):
17
- {% endif %}
18
- """Configuration for {{ app_name }} application."""
19
-
20
- default_auto_field = 'django.db.models.BigAutoField'
21
- name = '{{ app_name }}'
22
- verbose_name = '{{ app_name|title_case }}'
23
-
24
- {% if features.signals %}
25
- def ready(self):
26
- """Import signals when app is ready."""
27
- try:
28
- import {{ app_name }}.signals # noqa F401
29
- except ImportError:
30
- pass
31
- {% endif %}