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,333 @@
1
+ """
2
+ Django Best Practices Validator.
3
+
4
+ This module validates Django-specific patterns, conventions,
5
+ and best practices in generated code.
6
+ """
7
+
8
+ from typing import List, Dict, Any, Optional
9
+ import ast
10
+ import re
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+ from ...models.responses import GeneratedFile
15
+ from ..base import ServiceDependencies
16
+ from .models import ValidationIssue
17
+
18
+
19
+ class DjangoValidator(BaseModel):
20
+ """Validates Django best practices and conventions."""
21
+
22
+ django_rules: Dict[str, Dict[str, Any]] = Field(
23
+ default_factory=lambda: {
24
+ "model_meta_class": {
25
+ "description": "Models should have proper Meta class",
26
+ "severity": "warning"
27
+ },
28
+ "model_string_representation": {
29
+ "description": "Models should have __str__ method",
30
+ "severity": "warning"
31
+ },
32
+ "admin_registration": {
33
+ "description": "Admin classes should be properly registered",
34
+ "severity": "info"
35
+ },
36
+ "url_naming": {
37
+ "description": "URL patterns should have names",
38
+ "severity": "warning"
39
+ },
40
+ "view_docstrings": {
41
+ "description": "Views should have docstrings",
42
+ "severity": "info"
43
+ },
44
+ "form_validation": {
45
+ "description": "Forms should have proper validation",
46
+ "severity": "warning"
47
+ }
48
+ },
49
+ description="Django validation rules"
50
+ )
51
+
52
+ async def validate_django_practices(
53
+ self,
54
+ file: GeneratedFile,
55
+ dependencies: ServiceDependencies
56
+ ) -> List[ValidationIssue]:
57
+ """Validate Django best practices for a single file."""
58
+ issues = []
59
+
60
+ if file.file_type != "python":
61
+ return issues
62
+
63
+ try:
64
+ tree = ast.parse(file.content)
65
+
66
+ # Determine file type and apply appropriate validations
67
+ if "models.py" in file.path:
68
+ issues.extend(self._validate_models(file, tree, dependencies))
69
+ elif "admin.py" in file.path:
70
+ issues.extend(self._validate_admin(file, tree, dependencies))
71
+ elif "views.py" in file.path:
72
+ issues.extend(self._validate_views(file, tree, dependencies))
73
+ elif "urls.py" in file.path:
74
+ issues.extend(self._validate_urls(file, tree, dependencies))
75
+ elif "forms.py" in file.path:
76
+ issues.extend(self._validate_forms(file, tree, dependencies))
77
+ elif "serializers.py" in file.path:
78
+ issues.extend(self._validate_serializers(file, tree, dependencies))
79
+
80
+ except SyntaxError:
81
+ # Skip Django validation if syntax is invalid
82
+ pass
83
+ except Exception as e:
84
+ dependencies.log_error(f"Django validation failed for {file.path}", e)
85
+
86
+ return issues
87
+
88
+ def _validate_models(
89
+ self,
90
+ file: GeneratedFile,
91
+ tree: ast.AST,
92
+ dependencies: ServiceDependencies
93
+ ) -> List[ValidationIssue]:
94
+ """Validate Django model best practices."""
95
+ issues = []
96
+
97
+ for node in ast.walk(tree):
98
+ if isinstance(node, ast.ClassDef):
99
+ # Check if it's a model class
100
+ is_model = any(
101
+ isinstance(base, ast.Attribute) and
102
+ isinstance(base.value, ast.Name) and
103
+ base.value.id == "models" and
104
+ base.attr == "Model"
105
+ for base in node.bases
106
+ ) or any(
107
+ isinstance(base, ast.Name) and
108
+ "Model" in base.id
109
+ for base in node.bases
110
+ )
111
+
112
+ if is_model:
113
+ issues.extend(self._check_model_class(file, node))
114
+
115
+ return issues
116
+
117
+ def _check_model_class(
118
+ self,
119
+ file: GeneratedFile,
120
+ node: ast.ClassDef
121
+ ) -> List[ValidationIssue]:
122
+ """Check individual model class for best practices."""
123
+ issues = []
124
+
125
+ # Check for __str__ method
126
+ has_str_method = any(
127
+ isinstance(item, ast.FunctionDef) and item.name == "__str__"
128
+ for item in node.body
129
+ )
130
+
131
+ if not has_str_method:
132
+ issues.append(ValidationIssue(
133
+ severity="warning",
134
+ category="django",
135
+ message=f"Model '{node.name}' should have a __str__ method",
136
+ file_path=file.path,
137
+ line_number=node.lineno,
138
+ rule_id="model_str_method",
139
+ suggestion=f"Add a __str__ method to {node.name} that returns a meaningful string representation"
140
+ ))
141
+
142
+ # Check for Meta class
143
+ has_meta_class = any(
144
+ isinstance(item, ast.ClassDef) and item.name == "Meta"
145
+ for item in node.body
146
+ )
147
+
148
+ if not has_meta_class:
149
+ issues.append(ValidationIssue(
150
+ severity="info",
151
+ category="django",
152
+ message=f"Model '{node.name}' could benefit from a Meta class",
153
+ file_path=file.path,
154
+ line_number=node.lineno,
155
+ rule_id="model_meta_class",
156
+ suggestion=f"Consider adding a Meta class to {node.name} for verbose_name, ordering, etc."
157
+ ))
158
+
159
+ return issues
160
+
161
+ def _validate_admin(
162
+ self,
163
+ file: GeneratedFile,
164
+ tree: ast.AST,
165
+ dependencies: ServiceDependencies
166
+ ) -> List[ValidationIssue]:
167
+ """Validate Django admin best practices."""
168
+ issues = []
169
+
170
+ # Check for admin registrations
171
+ has_registrations = False
172
+
173
+ for node in ast.walk(tree):
174
+ if isinstance(node, ast.Call):
175
+ # Check for admin.site.register() calls
176
+ if (isinstance(node.func, ast.Attribute) and
177
+ isinstance(node.func.value, ast.Attribute) and
178
+ isinstance(node.func.value.value, ast.Name) and
179
+ node.func.value.value.id == "admin" and
180
+ node.func.value.attr == "site" and
181
+ node.func.attr == "register"):
182
+ has_registrations = True
183
+ elif isinstance(node, ast.FunctionDef):
184
+ # Check for @admin.register decorator
185
+ for decorator in node.decorator_list:
186
+ if (isinstance(decorator, ast.Call) and
187
+ isinstance(decorator.func, ast.Attribute) and
188
+ isinstance(decorator.func.value, ast.Name) and
189
+ decorator.func.value.id == "admin" and
190
+ decorator.func.attr == "register"):
191
+ has_registrations = True
192
+
193
+ if not has_registrations:
194
+ issues.append(ValidationIssue(
195
+ severity="info",
196
+ category="django",
197
+ message="No admin registrations found",
198
+ file_path=file.path,
199
+ line_number=1,
200
+ rule_id="admin_registration",
201
+ suggestion="Register models with admin.site.register() or @admin.register decorator"
202
+ ))
203
+
204
+ return issues
205
+
206
+ def _validate_views(
207
+ self,
208
+ file: GeneratedFile,
209
+ tree: ast.AST,
210
+ dependencies: ServiceDependencies
211
+ ) -> List[ValidationIssue]:
212
+ """Validate Django view best practices."""
213
+ issues = []
214
+
215
+ for node in ast.walk(tree):
216
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
217
+ # Check for docstrings
218
+ if not ast.get_docstring(node):
219
+ issues.append(ValidationIssue(
220
+ severity="info",
221
+ category="django",
222
+ message=f"View '{node.name}' should have a docstring",
223
+ file_path=file.path,
224
+ line_number=node.lineno,
225
+ rule_id="view_docstring",
226
+ suggestion=f"Add a docstring to {node.name} explaining its purpose"
227
+ ))
228
+
229
+ return issues
230
+
231
+ def _validate_urls(
232
+ self,
233
+ file: GeneratedFile,
234
+ tree: ast.AST,
235
+ dependencies: ServiceDependencies
236
+ ) -> List[ValidationIssue]:
237
+ """Validate Django URL patterns best practices."""
238
+ issues = []
239
+
240
+ # Check for named URL patterns
241
+ content = file.content
242
+ path_patterns = re.findall(r'path\([^)]+\)', content)
243
+
244
+ for pattern in path_patterns:
245
+ if 'name=' not in pattern:
246
+ issues.append(ValidationIssue(
247
+ severity="warning",
248
+ category="django",
249
+ message="URL pattern should have a name",
250
+ file_path=file.path,
251
+ line_number=1, # Would need more sophisticated line tracking
252
+ rule_id="url_naming",
253
+ suggestion="Add name='...' parameter to path() for reverse URL lookup"
254
+ ))
255
+
256
+ return issues
257
+
258
+ def _validate_forms(
259
+ self,
260
+ file: GeneratedFile,
261
+ tree: ast.AST,
262
+ dependencies: ServiceDependencies
263
+ ) -> List[ValidationIssue]:
264
+ """Validate Django form best practices."""
265
+ issues = []
266
+
267
+ for node in ast.walk(tree):
268
+ if isinstance(node, ast.ClassDef):
269
+ # Check if it's a form class
270
+ is_form = any(
271
+ isinstance(base, ast.Attribute) and
272
+ base.attr in ["Form", "ModelForm"]
273
+ for base in node.bases
274
+ )
275
+
276
+ if is_form:
277
+ # Check for clean methods
278
+ has_clean_methods = any(
279
+ isinstance(item, ast.FunctionDef) and
280
+ (item.name.startswith("clean_") or item.name == "clean")
281
+ for item in node.body
282
+ )
283
+
284
+ if not has_clean_methods:
285
+ issues.append(ValidationIssue(
286
+ severity="info",
287
+ category="django",
288
+ message=f"Form '{node.name}' could benefit from validation methods",
289
+ file_path=file.path,
290
+ line_number=node.lineno,
291
+ rule_id="form_validation",
292
+ suggestion=f"Consider adding clean_* methods to {node.name} for field validation"
293
+ ))
294
+
295
+ return issues
296
+
297
+ def _validate_serializers(
298
+ self,
299
+ file: GeneratedFile,
300
+ tree: ast.AST,
301
+ dependencies: ServiceDependencies
302
+ ) -> List[ValidationIssue]:
303
+ """Validate DRF serializer best practices."""
304
+ issues = []
305
+
306
+ for node in ast.walk(tree):
307
+ if isinstance(node, ast.ClassDef):
308
+ # Check if it's a serializer class
309
+ is_serializer = any(
310
+ isinstance(base, ast.Attribute) and
311
+ "Serializer" in base.attr
312
+ for base in node.bases
313
+ )
314
+
315
+ if is_serializer:
316
+ # Check for Meta class in ModelSerializer
317
+ has_meta = any(
318
+ isinstance(item, ast.ClassDef) and item.name == "Meta"
319
+ for item in node.body
320
+ )
321
+
322
+ if "ModelSerializer" in str(node.bases) and not has_meta:
323
+ issues.append(ValidationIssue(
324
+ severity="warning",
325
+ category="django",
326
+ message=f"ModelSerializer '{node.name}' should have a Meta class",
327
+ file_path=file.path,
328
+ line_number=node.lineno,
329
+ rule_id="serializer_meta",
330
+ suggestion=f"Add Meta class to {node.name} with model and fields"
331
+ ))
332
+
333
+ return issues
@@ -0,0 +1,242 @@
1
+ """
2
+ Main Validation Service for Django App Agent Module.
3
+
4
+ This service orchestrates comprehensive code validation including
5
+ syntax, Django best practices, security, and quality analysis.
6
+ """
7
+
8
+ from typing import List, Dict, Any, Optional
9
+ import asyncio
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+ from ...core.config import AgentConfig
14
+ from ...models.responses import GeneratedFile, QualityMetrics
15
+ from ..base import BaseService, ServiceDependencies
16
+ from .models import ValidationRequest, ValidationResult, ValidationIssue
17
+ from .syntax_validator import SyntaxValidator
18
+ from .django_validator import DjangoValidator
19
+ from .security_validator import SecurityValidator
20
+ from .quality_validator import QualityValidator
21
+
22
+
23
+ class ValidationService(BaseService[ValidationRequest, ValidationResult]):
24
+ """
25
+ Comprehensive code validation and quality analysis service.
26
+
27
+ Provides validation for:
28
+ - Python syntax and AST analysis
29
+ - Django best practices and conventions
30
+ - Security vulnerabilities and patterns
31
+ - Code quality metrics and standards
32
+ - Performance considerations
33
+ - Type hint completeness
34
+ """
35
+
36
+ def __init__(self, config: AgentConfig):
37
+ """Initialize validation service."""
38
+ super().__init__("validation", config)
39
+ self.config = config
40
+
41
+ # Initialize validators
42
+ self.syntax_validator = SyntaxValidator()
43
+ self.django_validator = DjangoValidator()
44
+ self.security_validator = SecurityValidator()
45
+ self.quality_validator = QualityValidator()
46
+
47
+ async def process(
48
+ self,
49
+ request: ValidationRequest,
50
+ dependencies: ServiceDependencies
51
+ ) -> ValidationResult:
52
+ """
53
+ Process comprehensive code validation.
54
+
55
+ Args:
56
+ request: Validation request with files and rules
57
+ dependencies: Service dependencies
58
+
59
+ Returns:
60
+ ValidationResult with issues and quality metrics
61
+ """
62
+ dependencies.log_operation(
63
+ "Starting code validation",
64
+ files_count=len(request.files),
65
+ validation_rules=request.validation_rules,
66
+ strict_mode=request.strict_mode
67
+ )
68
+
69
+ all_issues = []
70
+
71
+ try:
72
+ # Validate each file
73
+ for file in request.files:
74
+ file_issues = await self._validate_file(file, request, dependencies)
75
+ all_issues.extend(file_issues)
76
+
77
+ # Calculate quality metrics
78
+ quality_metrics = await self.quality_validator.calculate_quality_metrics(
79
+ request.files, dependencies
80
+ )
81
+
82
+ # Determine if validation passed
83
+ error_count = len([issue for issue in all_issues if issue.severity == "error"])
84
+ is_valid = error_count == 0 or not request.strict_mode
85
+
86
+ # Create summary
87
+ summary = self._create_validation_summary(all_issues, quality_metrics)
88
+
89
+ result = ValidationResult(
90
+ is_valid=is_valid,
91
+ quality_metrics=quality_metrics,
92
+ issues=all_issues,
93
+ summary=summary
94
+ )
95
+
96
+ dependencies.log_operation(
97
+ "Validation completed",
98
+ is_valid=is_valid,
99
+ total_issues=len(all_issues),
100
+ error_count=error_count,
101
+ warning_count=result.warning_count,
102
+ info_count=result.info_count,
103
+ overall_quality_score=quality_metrics.overall_score
104
+ )
105
+
106
+ return result
107
+
108
+ except Exception as e:
109
+ dependencies.log_error("Validation process failed", e)
110
+ raise
111
+
112
+ async def _validate_file(
113
+ self,
114
+ file: GeneratedFile,
115
+ request: ValidationRequest,
116
+ dependencies: ServiceDependencies
117
+ ) -> List[ValidationIssue]:
118
+ """Validate a single file with all applicable validators."""
119
+ issues = []
120
+
121
+ dependencies.log_operation(f"Validating file: {file.path}")
122
+
123
+ # Run validators based on requested rules
124
+ validation_tasks = []
125
+
126
+ if "syntax" in request.validation_rules:
127
+ validation_tasks.append(
128
+ self.syntax_validator.validate_syntax(file, dependencies)
129
+ )
130
+
131
+ if "django_best_practices" in request.validation_rules:
132
+ validation_tasks.append(
133
+ self.django_validator.validate_django_practices(file, dependencies)
134
+ )
135
+
136
+ if "security" in request.validation_rules:
137
+ validation_tasks.append(
138
+ self.security_validator.validate_security(file, dependencies)
139
+ )
140
+
141
+ if "quality" in request.validation_rules:
142
+ validation_tasks.append(
143
+ self.quality_validator.validate_quality(file, dependencies)
144
+ )
145
+
146
+ # Run all validations concurrently
147
+ if validation_tasks:
148
+ validation_results = await asyncio.gather(*validation_tasks, return_exceptions=True)
149
+
150
+ for result in validation_results:
151
+ if isinstance(result, Exception):
152
+ dependencies.log_error(f"Validator failed for {file.path}", result)
153
+ issues.append(ValidationIssue(
154
+ severity="error",
155
+ category="validation",
156
+ message=f"Validation failed: {result}",
157
+ file_path=file.path,
158
+ line_number=1,
159
+ rule_id="validation_error"
160
+ ))
161
+ else:
162
+ issues.extend(result)
163
+
164
+ return issues
165
+
166
+ def _create_validation_summary(
167
+ self,
168
+ issues: List[ValidationIssue],
169
+ quality_metrics: QualityMetrics
170
+ ) -> Dict[str, Any]:
171
+ """Create validation summary statistics."""
172
+ # Group issues by category and severity
173
+ by_category = {}
174
+ by_severity = {"error": 0, "warning": 0, "info": 0}
175
+
176
+ for issue in issues:
177
+ # Count by category
178
+ if issue.category not in by_category:
179
+ by_category[issue.category] = 0
180
+ by_category[issue.category] += 1
181
+
182
+ # Count by severity
183
+ if issue.severity in by_severity:
184
+ by_severity[issue.severity] += 1
185
+
186
+ # Calculate pass rates
187
+ total_checks = len(issues) if issues else 1
188
+ error_rate = by_severity["error"] / total_checks * 100
189
+ warning_rate = by_severity["warning"] / total_checks * 100
190
+
191
+ return {
192
+ "total_issues": len(issues),
193
+ "issues_by_severity": by_severity,
194
+ "issues_by_category": by_category,
195
+ "error_rate_percentage": error_rate,
196
+ "warning_rate_percentage": warning_rate,
197
+ "quality_score": quality_metrics.overall_score,
198
+ "type_safety_score": quality_metrics.type_safety_score,
199
+ "documentation_coverage": quality_metrics.documentation_coverage,
200
+ "maintainability_score": quality_metrics.maintainability_score,
201
+ "recommendations": self._generate_recommendations(issues, quality_metrics)
202
+ }
203
+
204
+ def _generate_recommendations(
205
+ self,
206
+ issues: List[ValidationIssue],
207
+ quality_metrics: QualityMetrics
208
+ ) -> List[str]:
209
+ """Generate actionable recommendations based on validation results."""
210
+ recommendations = []
211
+
212
+ # Analyze issue patterns
213
+ error_count = len([i for i in issues if i.severity == "error"])
214
+ warning_count = len([i for i in issues if i.severity == "warning"])
215
+
216
+ if error_count > 0:
217
+ recommendations.append(f"Fix {error_count} critical error(s) before deployment")
218
+
219
+ if warning_count > 5:
220
+ recommendations.append(f"Address {warning_count} warning(s) to improve code quality")
221
+
222
+ # Quality-based recommendations
223
+ if quality_metrics.type_safety_score < 7.0:
224
+ recommendations.append("Add type hints to improve type safety")
225
+
226
+ if quality_metrics.documentation_coverage < 6.0:
227
+ recommendations.append("Add docstrings to classes and functions")
228
+
229
+ if quality_metrics.code_complexity > 7.0:
230
+ recommendations.append("Reduce code complexity by breaking down large functions")
231
+
232
+ # Security recommendations
233
+ security_issues = [i for i in issues if i.category == "security"]
234
+ if security_issues:
235
+ recommendations.append("Review and address security-related issues")
236
+
237
+ # Django-specific recommendations
238
+ django_issues = [i for i in issues if i.category == "django"]
239
+ if len(django_issues) > 3:
240
+ recommendations.append("Follow Django best practices more consistently")
241
+
242
+ return recommendations[:5] # Limit to top 5 recommendations
@@ -0,0 +1,66 @@
1
+ """
2
+ Data Models for Validation Service.
3
+
4
+ This module defines the data structures used by the validation service
5
+ for requests, responses, and validation issues.
6
+ """
7
+
8
+ from typing import List, Dict, Any, Optional
9
+ from pydantic import BaseModel, Field, ConfigDict
10
+
11
+ from ...models.responses import QualityMetrics, GeneratedFile
12
+
13
+
14
+ class ValidationRequest(BaseModel):
15
+ """Request for code validation."""
16
+
17
+ model_config = ConfigDict(extra='forbid', validate_assignment=True)
18
+
19
+ files: List[GeneratedFile] = Field(description="Files to validate")
20
+ validation_rules: List[str] = Field(
21
+ default_factory=lambda: ["syntax", "django_best_practices", "security", "quality"],
22
+ description="Validation rules to apply"
23
+ )
24
+ strict_mode: bool = Field(default=False, description="Whether to use strict validation")
25
+ custom_rules: Dict[str, Any] = Field(default_factory=dict, description="Custom validation rules")
26
+
27
+
28
+ class ValidationIssue(BaseModel):
29
+ """Represents a validation issue."""
30
+
31
+ model_config = ConfigDict(extra='forbid', validate_assignment=True)
32
+
33
+ severity: str = Field(description="Issue severity: error, warning, info")
34
+ category: str = Field(description="Issue category: syntax, security, quality, etc.")
35
+ message: str = Field(description="Issue description message")
36
+ file_path: str = Field(description="Path to the file with the issue")
37
+ line_number: Optional[int] = Field(default=None, description="Line number of the issue")
38
+ column: Optional[int] = Field(default=None, description="Column number of the issue")
39
+ rule_id: str = Field(default="", description="Validation rule identifier")
40
+ suggestion: Optional[str] = Field(default=None, description="Suggested fix for the issue")
41
+
42
+
43
+ class ValidationResult(BaseModel):
44
+ """Result of code validation."""
45
+
46
+ model_config = ConfigDict(extra='forbid', validate_assignment=True)
47
+
48
+ is_valid: bool = Field(description="Whether the code passed validation")
49
+ quality_metrics: QualityMetrics = Field(description="Calculated quality metrics")
50
+ issues: List[ValidationIssue] = Field(default_factory=list, description="List of validation issues")
51
+ summary: Dict[str, Any] = Field(default_factory=dict, description="Validation summary")
52
+
53
+ @property
54
+ def error_count(self) -> int:
55
+ """Count of error-level issues."""
56
+ return len([i for i in self.issues if i.severity == "error"])
57
+
58
+ @property
59
+ def warning_count(self) -> int:
60
+ """Count of warning-level issues."""
61
+ return len([i for i in self.issues if i.severity == "warning"])
62
+
63
+ @property
64
+ def info_count(self) -> int:
65
+ """Count of info-level issues."""
66
+ return len([i for i in self.issues if i.severity == "info"])