django-cfg 1.3.9__py3-none-any.whl → 1.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/inlines.py +11 -5
  3. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  4. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  5. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  14. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  15. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  16. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  17. django_cfg/apps/payments/config/__init__.py +14 -15
  18. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  19. django_cfg/apps/payments/config/helpers.py +8 -13
  20. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  21. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  22. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  23. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  24. django_cfg/apps/payments/models/payments.py +94 -0
  25. django_cfg/apps/payments/services/core/base.py +4 -4
  26. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  27. django_cfg/apps/payments/services/providers/base.py +209 -3
  28. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  29. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  31. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  32. django_cfg/apps/payments/services/providers/registry.py +5 -5
  33. django_cfg/apps/payments/services/types/requests.py +19 -7
  34. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  35. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  36. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  37. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  38. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  39. django_cfg/apps/payments/urls.py +3 -2
  40. django_cfg/apps/payments/views/api/currencies.py +3 -0
  41. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  42. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  43. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  44. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  45. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  47. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  52. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  53. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  54. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  55. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  56. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  57. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  58. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  59. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  60. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  61. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  62. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  63. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  66. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  67. django_cfg/apps/tasks/urls.py +2 -2
  68. django_cfg/apps/tasks/urls_admin.py +2 -2
  69. django_cfg/apps/tasks/utils/__init__.py +1 -0
  70. django_cfg/apps/tasks/utils/simulator.py +356 -0
  71. django_cfg/apps/tasks/views/__init__.py +16 -0
  72. django_cfg/apps/tasks/views/api.py +569 -0
  73. django_cfg/apps/tasks/views/dashboard.py +58 -0
  74. django_cfg/core/integration/__init__.py +21 -0
  75. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  76. django_cfg/models/constance.py +0 -11
  77. django_cfg/models/payments.py +137 -3
  78. django_cfg/modules/django_tasks.py +54 -21
  79. django_cfg/registry/core.py +4 -9
  80. django_cfg/template_archive/django_sample.zip +0 -0
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/METADATA +2 -2
  82. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/RECORD +85 -153
  83. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  84. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  85. django_cfg/apps/payments/config/constance/fields.py +0 -69
  86. django_cfg/apps/payments/config/constance/settings.py +0 -160
  87. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  88. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  89. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  90. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  91. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  92. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  93. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  94. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  95. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  96. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  97. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  98. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  99. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  100. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  101. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  102. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  103. django_cfg/apps/tasks/views.py +0 -461
  104. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  105. django_cfg/management/commands/app_agent_generate.py +0 -342
  106. django_cfg/management/commands/app_agent_info.py +0 -308
  107. django_cfg/management/commands/auto_generate.py +0 -486
  108. django_cfg/modules/django_app_agent/__init__.py +0 -87
  109. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  110. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  111. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  112. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  113. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  114. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  119. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  120. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  121. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  122. django_cfg/modules/django_app_agent/core/config.py +0 -300
  123. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  124. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  125. django_cfg/modules/django_app_agent/models/base.py +0 -283
  126. django_cfg/modules/django_app_agent/models/context.py +0 -496
  127. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  128. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  129. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  130. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  131. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  132. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  133. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  134. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  135. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  136. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  137. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  138. django_cfg/modules/django_app_agent/services/base.py +0 -437
  139. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  140. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  141. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  142. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  143. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  144. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  145. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  146. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  147. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  148. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  149. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  150. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  151. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  152. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  153. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  154. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  155. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  156. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  157. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  158. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  159. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  160. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  161. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  171. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  172. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  173. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  174. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  175. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  176. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  177. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  178. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  179. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  180. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  181. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  182. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  183. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  184. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  185. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/WHEEL +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/entry_points.txt +0 -0
  188. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,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)