django-cfg 1.3.9__py3-none-any.whl → 1.3.11__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 (187) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  3. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  4. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  5. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  13. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  14. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  15. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  16. django_cfg/apps/payments/config/__init__.py +14 -15
  17. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  18. django_cfg/apps/payments/config/helpers.py +8 -13
  19. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  20. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  21. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  22. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  23. django_cfg/apps/payments/models/payments.py +94 -0
  24. django_cfg/apps/payments/services/core/base.py +4 -4
  25. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  26. django_cfg/apps/payments/services/providers/base.py +209 -3
  27. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  28. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  29. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  31. django_cfg/apps/payments/services/providers/registry.py +5 -5
  32. django_cfg/apps/payments/services/types/requests.py +19 -7
  33. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  34. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  35. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  36. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  37. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  38. django_cfg/apps/payments/urls.py +3 -2
  39. django_cfg/apps/payments/views/api/currencies.py +3 -0
  40. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  41. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  42. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  43. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  44. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  45. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  47. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  52. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  53. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  54. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  55. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  56. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  57. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  58. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  59. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  60. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  61. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  62. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  63. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  66. django_cfg/apps/tasks/urls.py +2 -2
  67. django_cfg/apps/tasks/urls_admin.py +2 -2
  68. django_cfg/apps/tasks/utils/__init__.py +1 -0
  69. django_cfg/apps/tasks/utils/simulator.py +356 -0
  70. django_cfg/apps/tasks/views/__init__.py +16 -0
  71. django_cfg/apps/tasks/views/api.py +569 -0
  72. django_cfg/apps/tasks/views/dashboard.py +58 -0
  73. django_cfg/core/integration/__init__.py +21 -0
  74. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  75. django_cfg/models/constance.py +0 -11
  76. django_cfg/models/payments.py +137 -3
  77. django_cfg/modules/django_tasks.py +54 -21
  78. django_cfg/registry/core.py +4 -9
  79. django_cfg/template_archive/django_sample.zip +0 -0
  80. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
  82. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  83. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  84. django_cfg/apps/payments/config/constance/fields.py +0 -69
  85. django_cfg/apps/payments/config/constance/settings.py +0 -160
  86. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  87. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  88. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  89. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  90. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  91. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  92. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  93. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  94. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  95. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  96. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  97. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  98. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  100. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  101. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  102. django_cfg/apps/tasks/views.py +0 -461
  103. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  104. django_cfg/management/commands/app_agent_generate.py +0 -342
  105. django_cfg/management/commands/app_agent_info.py +0 -308
  106. django_cfg/management/commands/auto_generate.py +0 -486
  107. django_cfg/modules/django_app_agent/__init__.py +0 -87
  108. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  109. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  110. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  111. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  112. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  113. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  114. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  119. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  120. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  121. django_cfg/modules/django_app_agent/core/config.py +0 -300
  122. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  123. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  124. django_cfg/modules/django_app_agent/models/base.py +0 -283
  125. django_cfg/modules/django_app_agent/models/context.py +0 -496
  126. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  127. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  128. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  129. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  130. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  131. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  132. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  133. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  134. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  135. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  136. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  137. django_cfg/modules/django_app_agent/services/base.py +0 -437
  138. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  139. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  140. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  141. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  142. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  143. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  144. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  145. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  146. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  147. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  148. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  149. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  150. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  151. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  152. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  153. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  154. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  155. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  156. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  157. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  158. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  159. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  160. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  161. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  171. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  172. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  173. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  174. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  175. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  176. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  177. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  178. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  179. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  180. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  181. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  182. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  183. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  184. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  185. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,352 +0,0 @@
1
- """
2
- Code Quality Validator for Django App Agent.
3
-
4
- This module validates code quality metrics, patterns,
5
- and maintainability aspects of 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, QualityMetrics
15
- from ..base import ServiceDependencies
16
- from .models import ValidationIssue
17
-
18
-
19
- class QualityValidator(BaseModel):
20
- """Validates code quality and maintainability patterns."""
21
-
22
- quality_rules: Dict[str, Dict[str, Any]] = Field(
23
- default_factory=lambda: {
24
- "function_complexity": {
25
- "description": "Functions should not be overly complex",
26
- "severity": "warning",
27
- "max_complexity": 10
28
- },
29
- "line_length": {
30
- "description": "Lines should not exceed reasonable length",
31
- "severity": "warning",
32
- "max_length": 120
33
- },
34
- "docstring_coverage": {
35
- "description": "Classes and functions should have docstrings",
36
- "severity": "info"
37
- },
38
- "type_hints": {
39
- "description": "Functions should have type hints",
40
- "severity": "info"
41
- },
42
- "naming_conventions": {
43
- "description": "Follow Python naming conventions",
44
- "severity": "warning"
45
- },
46
- "code_duplication": {
47
- "description": "Avoid code duplication",
48
- "severity": "warning"
49
- }
50
- },
51
- description="Code quality validation rules"
52
- )
53
-
54
- async def validate_quality(
55
- self,
56
- file: GeneratedFile,
57
- dependencies: ServiceDependencies
58
- ) -> List[ValidationIssue]:
59
- """Validate code quality for a single file."""
60
- issues = []
61
-
62
- if file.file_type != "python":
63
- return issues
64
-
65
- try:
66
- tree = ast.parse(file.content)
67
-
68
- # Apply quality checks
69
- issues.extend(self._check_complexity(file, tree, dependencies))
70
- issues.extend(self._check_line_length(file, dependencies))
71
- issues.extend(self._check_docstrings(file, tree, dependencies))
72
- issues.extend(self._check_type_hints(file, tree, dependencies))
73
- issues.extend(self._check_naming_conventions(file, tree, dependencies))
74
- issues.extend(self._check_code_duplication(file, tree, dependencies))
75
-
76
- except SyntaxError:
77
- # Skip quality validation if syntax is invalid
78
- pass
79
- except Exception as e:
80
- dependencies.log_error(f"Quality validation failed for {file.path}", e)
81
-
82
- return issues
83
-
84
- async def calculate_quality_metrics(
85
- self,
86
- files: List[GeneratedFile],
87
- dependencies: ServiceDependencies
88
- ) -> QualityMetrics:
89
- """Calculate comprehensive quality metrics."""
90
- total_lines = 0
91
- total_functions = 0
92
- functions_with_docstrings = 0
93
- functions_with_type_hints = 0
94
- classes_with_docstrings = 0
95
- total_classes = 0
96
- complexity_scores = []
97
-
98
- for file in files:
99
- if file.file_type != "python":
100
- continue
101
-
102
- try:
103
- tree = ast.parse(file.content)
104
- lines = file.content.split('\n')
105
- total_lines += len([line for line in lines if line.strip()])
106
-
107
- for node in ast.walk(tree):
108
- if isinstance(node, ast.FunctionDef):
109
- total_functions += 1
110
-
111
- # Check docstring
112
- if ast.get_docstring(node):
113
- functions_with_docstrings += 1
114
-
115
- # Check type hints
116
- if self._has_type_hints(node):
117
- functions_with_type_hints += 1
118
-
119
- # Calculate complexity
120
- complexity = self._calculate_cyclomatic_complexity(node)
121
- complexity_scores.append(complexity)
122
-
123
- elif isinstance(node, ast.ClassDef):
124
- total_classes += 1
125
- if ast.get_docstring(node):
126
- classes_with_docstrings += 1
127
-
128
- except SyntaxError:
129
- continue
130
-
131
- # Calculate metrics
132
- docstring_coverage = 0.0
133
- if total_functions + total_classes > 0:
134
- docstring_coverage = (functions_with_docstrings + classes_with_docstrings) / (total_functions + total_classes) * 100
135
-
136
- type_hint_coverage = 0.0
137
- if total_functions > 0:
138
- type_hint_coverage = functions_with_type_hints / total_functions * 100
139
-
140
- avg_complexity = sum(complexity_scores) / len(complexity_scores) if complexity_scores else 0
141
-
142
- # Calculate overall scores
143
- overall_score = (docstring_coverage * 0.2 + type_hint_coverage * 0.3 +
144
- max(0, 100 - avg_complexity * 10) * 0.5) / 10
145
-
146
- return QualityMetrics(
147
- overall_score=min(10.0, max(0.0, overall_score)),
148
- type_safety_score=min(10.0, type_hint_coverage / 10),
149
- pattern_consistency=8.5, # Would need more sophisticated analysis
150
- code_complexity=max(0.0, 10.0 - avg_complexity),
151
- test_coverage=0.0, # Would need test analysis
152
- documentation_coverage=min(100.0, docstring_coverage), # Keep as percentage
153
- performance_score=8.0, # Would need performance analysis
154
- security_score=8.5, # Would need security analysis
155
- maintainability_score=min(10.0, max(0.0, overall_score))
156
- )
157
-
158
- def _check_complexity(
159
- self,
160
- file: GeneratedFile,
161
- tree: ast.AST,
162
- dependencies: ServiceDependencies
163
- ) -> List[ValidationIssue]:
164
- """Check cyclomatic complexity of functions."""
165
- issues = []
166
- max_complexity = self.quality_rules["function_complexity"]["max_complexity"]
167
-
168
- for node in ast.walk(tree):
169
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
170
- complexity = self._calculate_cyclomatic_complexity(node)
171
-
172
- if complexity > max_complexity:
173
- issues.append(ValidationIssue(
174
- severity="warning",
175
- category="quality",
176
- message=f"Function '{node.name}' has high complexity ({complexity})",
177
- file_path=file.path,
178
- line_number=node.lineno,
179
- rule_id="high_complexity",
180
- suggestion=f"Consider breaking down {node.name} into smaller functions"
181
- ))
182
-
183
- return issues
184
-
185
- def _check_line_length(
186
- self,
187
- file: GeneratedFile,
188
- dependencies: ServiceDependencies
189
- ) -> List[ValidationIssue]:
190
- """Check for overly long lines."""
191
- issues = []
192
- max_length = self.quality_rules["line_length"]["max_length"]
193
-
194
- lines = file.content.split('\n')
195
- for i, line in enumerate(lines, 1):
196
- if len(line) > max_length:
197
- issues.append(ValidationIssue(
198
- severity="info",
199
- category="quality",
200
- message=f"Line {i} exceeds {max_length} characters ({len(line)})",
201
- file_path=file.path,
202
- line_number=i,
203
- rule_id="line_too_long",
204
- suggestion="Break long lines for better readability"
205
- ))
206
-
207
- return issues
208
-
209
- def _check_docstrings(
210
- self,
211
- file: GeneratedFile,
212
- tree: ast.AST,
213
- dependencies: ServiceDependencies
214
- ) -> List[ValidationIssue]:
215
- """Check for missing docstrings."""
216
- issues = []
217
-
218
- for node in ast.walk(tree):
219
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
220
- if not ast.get_docstring(node):
221
- node_type = "Class" if isinstance(node, ast.ClassDef) else "Function"
222
- issues.append(ValidationIssue(
223
- severity="info",
224
- category="quality",
225
- message=f"{node_type} '{node.name}' lacks docstring",
226
- file_path=file.path,
227
- line_number=node.lineno,
228
- rule_id="missing_docstring",
229
- suggestion=f"Add a docstring to {node.name} explaining its purpose"
230
- ))
231
-
232
- return issues
233
-
234
- def _check_type_hints(
235
- self,
236
- file: GeneratedFile,
237
- tree: ast.AST,
238
- dependencies: ServiceDependencies
239
- ) -> List[ValidationIssue]:
240
- """Check for missing type hints."""
241
- issues = []
242
-
243
- for node in ast.walk(tree):
244
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
245
- if not self._has_type_hints(node):
246
- issues.append(ValidationIssue(
247
- severity="info",
248
- category="quality",
249
- message=f"Function '{node.name}' lacks type hints",
250
- file_path=file.path,
251
- line_number=node.lineno,
252
- rule_id="missing_type_hints",
253
- suggestion=f"Add type hints to {node.name} parameters and return value"
254
- ))
255
-
256
- return issues
257
-
258
- def _check_naming_conventions(
259
- self,
260
- file: GeneratedFile,
261
- tree: ast.AST,
262
- dependencies: ServiceDependencies
263
- ) -> List[ValidationIssue]:
264
- """Check Python naming conventions."""
265
- issues = []
266
-
267
- for node in ast.walk(tree):
268
- if isinstance(node, ast.ClassDef):
269
- # Class names should be PascalCase
270
- if not re.match(r'^[A-Z][a-zA-Z0-9]*$', node.name):
271
- issues.append(ValidationIssue(
272
- severity="warning",
273
- category="quality",
274
- message=f"Class '{node.name}' should use PascalCase",
275
- file_path=file.path,
276
- line_number=node.lineno,
277
- rule_id="class_naming",
278
- suggestion=f"Rename {node.name} to follow PascalCase convention"
279
- ))
280
-
281
- elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
282
- # Function names should be snake_case
283
- if not re.match(r'^[a-z_][a-z0-9_]*$', node.name) and not node.name.startswith('__'):
284
- issues.append(ValidationIssue(
285
- severity="warning",
286
- category="quality",
287
- message=f"Function '{node.name}' should use snake_case",
288
- file_path=file.path,
289
- line_number=node.lineno,
290
- rule_id="function_naming",
291
- suggestion=f"Rename {node.name} to follow snake_case convention"
292
- ))
293
-
294
- return issues
295
-
296
- def _check_code_duplication(
297
- self,
298
- file: GeneratedFile,
299
- tree: ast.AST,
300
- dependencies: ServiceDependencies
301
- ) -> List[ValidationIssue]:
302
- """Check for code duplication patterns."""
303
- issues = []
304
-
305
- # Simple duplication check - look for identical function bodies
306
- function_bodies = {}
307
-
308
- for node in ast.walk(tree):
309
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
310
- # Convert function body to string for comparison
311
- body_str = ast.dump(node.body)
312
-
313
- if body_str in function_bodies:
314
- issues.append(ValidationIssue(
315
- severity="warning",
316
- category="quality",
317
- message=f"Function '{node.name}' has duplicate body with '{function_bodies[body_str]}'",
318
- file_path=file.path,
319
- line_number=node.lineno,
320
- rule_id="code_duplication",
321
- suggestion=f"Consider extracting common logic from {node.name} and {function_bodies[body_str]}"
322
- ))
323
- else:
324
- function_bodies[body_str] = node.name
325
-
326
- return issues
327
-
328
- def _calculate_cyclomatic_complexity(self, node: ast.AST) -> int:
329
- """Calculate cyclomatic complexity of a function."""
330
- complexity = 1 # Base complexity
331
-
332
- for child in ast.walk(node):
333
- if isinstance(child, (ast.If, ast.While, ast.For, ast.AsyncFor)):
334
- complexity += 1
335
- elif isinstance(child, ast.ExceptHandler):
336
- complexity += 1
337
- elif isinstance(child, (ast.And, ast.Or)):
338
- complexity += 1
339
- elif isinstance(child, ast.comprehension):
340
- complexity += 1
341
-
342
- return complexity
343
-
344
- def _has_type_hints(self, node: ast.FunctionDef) -> bool:
345
- """Check if function has type hints."""
346
- # Check return annotation
347
- has_return_hint = node.returns is not None
348
-
349
- # Check parameter annotations
350
- has_param_hints = any(arg.annotation is not None for arg in node.args.args)
351
-
352
- return has_return_hint or has_param_hints
@@ -1,272 +0,0 @@
1
- """
2
- Security Validator for Django App Agent.
3
-
4
- This module validates security patterns and identifies potential
5
- security vulnerabilities in generated Django 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 SecurityValidator(BaseModel):
20
- """Validates security patterns and identifies vulnerabilities."""
21
-
22
- security_rules: Dict[str, Dict[str, Any]] = Field(
23
- default_factory=lambda: {
24
- "sql_injection": {
25
- "description": "Prevent SQL injection vulnerabilities",
26
- "severity": "error"
27
- },
28
- "xss_protection": {
29
- "description": "Ensure XSS protection in templates",
30
- "severity": "error"
31
- },
32
- "csrf_protection": {
33
- "description": "CSRF protection should be enabled",
34
- "severity": "warning"
35
- },
36
- "secure_settings": {
37
- "description": "Security settings should be properly configured",
38
- "severity": "warning"
39
- },
40
- "input_validation": {
41
- "description": "User input should be properly validated",
42
- "severity": "warning"
43
- },
44
- "authentication_required": {
45
- "description": "Sensitive views should require authentication",
46
- "severity": "warning"
47
- }
48
- },
49
- description="Security validation rules"
50
- )
51
-
52
- async def validate_security(
53
- self,
54
- file: GeneratedFile,
55
- dependencies: ServiceDependencies
56
- ) -> List[ValidationIssue]:
57
- """Validate security patterns 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
- # Apply security checks
67
- issues.extend(self._check_sql_injection(file, tree, dependencies))
68
- issues.extend(self._check_authentication(file, tree, dependencies))
69
- issues.extend(self._check_input_validation(file, tree, dependencies))
70
- issues.extend(self._check_dangerous_functions(file, tree, dependencies))
71
-
72
- except SyntaxError:
73
- # Skip security validation if syntax is invalid
74
- pass
75
- except Exception as e:
76
- dependencies.log_error(f"Security validation failed for {file.path}", e)
77
-
78
- return issues
79
-
80
- def _check_sql_injection(
81
- self,
82
- file: GeneratedFile,
83
- tree: ast.AST,
84
- dependencies: ServiceDependencies
85
- ) -> List[ValidationIssue]:
86
- """Check for potential SQL injection vulnerabilities."""
87
- issues = []
88
-
89
- # Look for raw SQL usage
90
- for node in ast.walk(tree):
91
- if isinstance(node, ast.Call):
92
- # Check for raw() method calls
93
- if (isinstance(node.func, ast.Attribute) and
94
- node.func.attr == "raw"):
95
- issues.append(ValidationIssue(
96
- severity="warning",
97
- category="security",
98
- message="Raw SQL query detected - ensure parameters are properly escaped",
99
- file_path=file.path,
100
- line_number=node.lineno,
101
- rule_id="raw_sql_usage",
102
- suggestion="Use Django ORM or parameterized queries instead of raw SQL"
103
- ))
104
-
105
- # Check for extra() method with potentially unsafe parameters
106
- if (isinstance(node.func, ast.Attribute) and
107
- node.func.attr == "extra"):
108
- issues.append(ValidationIssue(
109
- severity="warning",
110
- category="security",
111
- message="QuerySet.extra() usage detected - ensure SQL is safe",
112
- file_path=file.path,
113
- line_number=node.lineno,
114
- rule_id="extra_sql_usage",
115
- suggestion="Prefer Django ORM methods over extra() when possible"
116
- ))
117
-
118
- # Check for string formatting in SQL-like contexts
119
- content = file.content
120
- if re.search(r'["\'].*%s.*["\'].*%', content):
121
- issues.append(ValidationIssue(
122
- severity="warning",
123
- category="security",
124
- message="String formatting in SQL-like context detected",
125
- file_path=file.path,
126
- line_number=1,
127
- rule_id="string_format_sql",
128
- suggestion="Use parameterized queries instead of string formatting"
129
- ))
130
-
131
- return issues
132
-
133
- def _check_authentication(
134
- self,
135
- file: GeneratedFile,
136
- tree: ast.AST,
137
- dependencies: ServiceDependencies
138
- ) -> List[ValidationIssue]:
139
- """Check for proper authentication patterns."""
140
- issues = []
141
-
142
- # Check views for authentication decorators/mixins
143
- if "views.py" in file.path:
144
- for node in ast.walk(tree):
145
- if isinstance(node, ast.FunctionDef):
146
- # Check for authentication decorators
147
- has_auth_decorator = any(
148
- (isinstance(dec, ast.Name) and dec.id == "login_required") or
149
- (isinstance(dec, ast.Attribute) and dec.attr == "login_required")
150
- for dec in node.decorator_list
151
- )
152
-
153
- # Skip views that clearly don't need authentication
154
- if not has_auth_decorator and not self._is_public_view(node.name):
155
- issues.append(ValidationIssue(
156
- severity="info",
157
- category="security",
158
- message=f"View '{node.name}' may need authentication",
159
- file_path=file.path,
160
- line_number=node.lineno,
161
- rule_id="missing_authentication",
162
- suggestion=f"Consider adding @login_required decorator to {node.name} if it handles sensitive data"
163
- ))
164
-
165
- elif isinstance(node, ast.ClassDef):
166
- # Check class-based views for LoginRequiredMixin
167
- has_login_mixin = any(
168
- isinstance(base, ast.Name) and base.id == "LoginRequiredMixin"
169
- for base in node.bases
170
- )
171
-
172
- if not has_login_mixin and not self._is_public_view(node.name):
173
- issues.append(ValidationIssue(
174
- severity="info",
175
- category="security",
176
- message=f"View class '{node.name}' may need authentication",
177
- file_path=file.path,
178
- line_number=node.lineno,
179
- rule_id="missing_login_mixin",
180
- suggestion=f"Consider adding LoginRequiredMixin to {node.name} if it handles sensitive data"
181
- ))
182
-
183
- return issues
184
-
185
- def _check_input_validation(
186
- self,
187
- file: GeneratedFile,
188
- tree: ast.AST,
189
- dependencies: ServiceDependencies
190
- ) -> List[ValidationIssue]:
191
- """Check for proper input validation."""
192
- issues = []
193
-
194
- # Check forms for validation methods
195
- if "forms.py" in file.path:
196
- for node in ast.walk(tree):
197
- if isinstance(node, ast.ClassDef):
198
- is_form = any(
199
- isinstance(base, ast.Attribute) and
200
- base.attr in ["Form", "ModelForm"]
201
- for base in node.bases
202
- )
203
-
204
- if is_form:
205
- # Check for clean methods
206
- has_validation = any(
207
- isinstance(item, ast.FunctionDef) and
208
- (item.name.startswith("clean_") or item.name == "clean")
209
- for item in node.body
210
- )
211
-
212
- if not has_validation:
213
- issues.append(ValidationIssue(
214
- severity="info",
215
- category="security",
216
- message=f"Form '{node.name}' lacks custom validation",
217
- file_path=file.path,
218
- line_number=node.lineno,
219
- rule_id="missing_form_validation",
220
- suggestion=f"Consider adding clean_* methods to {node.name} for input validation"
221
- ))
222
-
223
- return issues
224
-
225
- def _check_dangerous_functions(
226
- self,
227
- file: GeneratedFile,
228
- tree: ast.AST,
229
- dependencies: ServiceDependencies
230
- ) -> List[ValidationIssue]:
231
- """Check for usage of potentially dangerous functions."""
232
- issues = []
233
-
234
- dangerous_functions = {
235
- "eval": "Use of eval() can execute arbitrary code",
236
- "exec": "Use of exec() can execute arbitrary code",
237
- "compile": "Use of compile() with user input can be dangerous",
238
- "__import__": "Dynamic imports can be security risks"
239
- }
240
-
241
- for node in ast.walk(tree):
242
- if isinstance(node, ast.Call):
243
- func_name = None
244
-
245
- if isinstance(node.func, ast.Name):
246
- func_name = node.func.id
247
- elif isinstance(node.func, ast.Attribute):
248
- func_name = node.func.attr
249
-
250
- if func_name in dangerous_functions:
251
- issues.append(ValidationIssue(
252
- severity="error",
253
- category="security",
254
- message=f"Dangerous function '{func_name}' detected: {dangerous_functions[func_name]}",
255
- file_path=file.path,
256
- line_number=node.lineno,
257
- rule_id="dangerous_function",
258
- suggestion=f"Avoid using {func_name}() or ensure input is properly sanitized"
259
- ))
260
-
261
- return issues
262
-
263
- def _is_public_view(self, view_name: str) -> bool:
264
- """Check if a view is likely meant to be public."""
265
- public_patterns = [
266
- "index", "home", "landing", "about", "contact",
267
- "login", "logout", "register", "signup",
268
- "public", "api", "health", "status"
269
- ]
270
-
271
- view_lower = view_name.lower()
272
- return any(pattern in view_lower for pattern in public_patterns)