django-cfg 1.3.7__py3-none-any.whl → 1.3.9__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 (251) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/config.py +1 -1
  97. django_cfg/core/config.py +10 -5
  98. django_cfg/core/generation.py +1 -1
  99. django_cfg/management/commands/__init__.py +13 -1
  100. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  101. django_cfg/management/commands/app_agent_generate.py +342 -0
  102. django_cfg/management/commands/app_agent_info.py +308 -0
  103. django_cfg/management/commands/migrate_all.py +9 -3
  104. django_cfg/management/commands/migrator.py +11 -6
  105. django_cfg/management/commands/rundramatiq.py +3 -2
  106. django_cfg/middleware/__init__.py +0 -2
  107. django_cfg/models/api_keys.py +115 -0
  108. django_cfg/modules/django_admin/__init__.py +64 -0
  109. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  110. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  111. django_cfg/modules/django_admin/decorators/display.py +106 -0
  112. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  113. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  114. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  115. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  116. django_cfg/modules/django_admin/models/__init__.py +20 -0
  117. django_cfg/modules/django_admin/models/action_models.py +33 -0
  118. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  119. django_cfg/modules/django_admin/models/base.py +26 -0
  120. django_cfg/modules/django_admin/models/display_models.py +31 -0
  121. django_cfg/modules/django_admin/utils/badges.py +159 -0
  122. django_cfg/modules/django_admin/utils/displays.py +247 -0
  123. django_cfg/modules/django_app_agent/__init__.py +87 -0
  124. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  125. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  126. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  127. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  128. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  129. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  130. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  135. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  136. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  137. django_cfg/modules/django_app_agent/core/config.py +300 -0
  138. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  139. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  140. django_cfg/modules/django_app_agent/models/base.py +283 -0
  141. django_cfg/modules/django_app_agent/models/context.py +496 -0
  142. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  143. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  144. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  145. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  146. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  147. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  153. django_cfg/modules/django_app_agent/services/base.py +437 -0
  154. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  160. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  165. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  171. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  172. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  188. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  195. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  196. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  197. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  198. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  199. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  200. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  201. django_cfg/modules/django_currency/__init__.py +2 -2
  202. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  204. django_cfg/modules/django_currency/core/converter.py +12 -12
  205. django_cfg/modules/django_currency/database/__init__.py +2 -2
  206. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  207. django_cfg/modules/django_llm/llm/client.py +10 -2
  208. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  209. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  210. django_cfg/modules/django_unfold/dashboard.py +14 -13
  211. django_cfg/modules/django_unfold/models/config.py +1 -1
  212. django_cfg/registry/core.py +3 -0
  213. django_cfg/registry/third_party.py +2 -2
  214. django_cfg/template_archive/django_sample.zip +0 -0
  215. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  216. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/RECORD +223 -117
  217. django_cfg/apps/accounts/admin/activity.py +0 -96
  218. django_cfg/apps/accounts/admin/group.py +0 -17
  219. django_cfg/apps/accounts/admin/otp.py +0 -59
  220. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  221. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  222. django_cfg/apps/accounts/admin/user.py +0 -300
  223. django_cfg/apps/agents/core/agent.py +0 -281
  224. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  225. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  226. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  227. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  228. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  229. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  230. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  231. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  232. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  236. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  237. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  239. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  242. django_cfg/apps/tasks/admin.py +0 -320
  243. django_cfg/middleware/static_nocache.py +0 -55
  244. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  245. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  249. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  250. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  251. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,236 @@
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())
@@ -0,0 +1,159 @@
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)
@@ -0,0 +1,36 @@
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")
@@ -0,0 +1,100 @@
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)
@@ -0,0 +1,105 @@
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 %}
@@ -0,0 +1,31 @@
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 %}