django-cfg 1.3.5__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/__init__.py +24 -8
- django_cfg/apps/accounts/admin/activity_admin.py +146 -0
- django_cfg/apps/accounts/admin/filters.py +98 -22
- django_cfg/apps/accounts/admin/group_admin.py +86 -0
- django_cfg/apps/accounts/admin/inlines.py +42 -13
- django_cfg/apps/accounts/admin/otp_admin.py +115 -0
- django_cfg/apps/accounts/admin/registration_admin.py +173 -0
- django_cfg/apps/accounts/admin/resources.py +123 -19
- django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
- django_cfg/apps/accounts/admin/user_admin.py +362 -0
- django_cfg/apps/agents/admin/__init__.py +17 -4
- django_cfg/apps/agents/admin/execution_admin.py +204 -183
- django_cfg/apps/agents/admin/registry_admin.py +230 -255
- django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
- django_cfg/apps/agents/core/__init__.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +221 -0
- django_cfg/apps/agents/core/exceptions.py +14 -0
- django_cfg/apps/agents/core/orchestrator.py +18 -3
- django_cfg/apps/knowbase/admin/__init__.py +1 -1
- django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
- django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
- django_cfg/apps/knowbase/admin/document_admin.py +269 -262
- django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
- django_cfg/apps/knowbase/config/settings.py +21 -4
- django_cfg/apps/knowbase/views/chat_views.py +3 -0
- django_cfg/apps/leads/admin/__init__.py +3 -1
- django_cfg/apps/leads/admin/leads_admin.py +235 -35
- django_cfg/apps/maintenance/admin/__init__.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
- django_cfg/apps/maintenance/admin/log_admin.py +143 -61
- django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
- django_cfg/apps/maintenance/admin/site_admin.py +213 -352
- django_cfg/apps/newsletter/admin/__init__.py +29 -2
- django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
- django_cfg/apps/payments/admin/__init__.py +18 -27
- django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
- django_cfg/apps/payments/admin/balance_admin.py +166 -632
- django_cfg/apps/payments/admin/currencies_admin.py +235 -607
- django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
- django_cfg/apps/payments/admin/filters.py +83 -3
- django_cfg/apps/payments/admin/networks_admin.py +258 -0
- django_cfg/apps/payments/admin/payments_admin.py +171 -461
- django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
- django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
- django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
- django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
- django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
- django_cfg/apps/payments/middleware/api_access.py +32 -6
- django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
- django_cfg/apps/payments/models/balance.py +12 -0
- django_cfg/apps/payments/models/currencies.py +106 -32
- django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
- django_cfg/apps/payments/services/core/currency_service.py +35 -28
- django_cfg/apps/payments/services/core/payment_service.py +1 -1
- django_cfg/apps/payments/services/providers/__init__.py +3 -0
- django_cfg/apps/payments/services/providers/base.py +95 -39
- django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
- django_cfg/apps/payments/services/providers/models/base.py +122 -0
- django_cfg/apps/payments/services/providers/models/providers.py +87 -0
- django_cfg/apps/payments/services/providers/models/universal.py +48 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
- django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
- django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
- django_cfg/apps/payments/services/providers/registry.py +4 -32
- django_cfg/apps/payments/services/providers/sync_service.py +277 -0
- django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
- django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
- django_cfg/apps/payments/tasks/__init__.py +39 -0
- django_cfg/apps/payments/tasks/types.py +73 -0
- django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
- django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
- django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
- django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
- django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
- django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
- django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
- django_cfg/apps/payments/urls_admin.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +5 -5
- django_cfg/apps/payments/views/overview/services.py +2 -2
- django_cfg/apps/payments/views/serializers/currencies.py +4 -3
- django_cfg/apps/support/admin/__init__.py +10 -1
- django_cfg/apps/support/admin/support_admin.py +338 -141
- django_cfg/apps/tasks/admin/__init__.py +11 -0
- django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
- django_cfg/apps/urls.py +1 -2
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +10 -5
- django_cfg/core/generation.py +1 -1
- django_cfg/management/commands/__init__.py +13 -1
- django_cfg/management/commands/app_agent_diagnose.py +470 -0
- django_cfg/management/commands/app_agent_generate.py +342 -0
- django_cfg/management/commands/app_agent_info.py +308 -0
- django_cfg/management/commands/migrate_all.py +9 -3
- django_cfg/management/commands/migrator.py +11 -6
- django_cfg/management/commands/rundramatiq.py +3 -2
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/models/api_keys.py +115 -0
- django_cfg/modules/django_admin/__init__.py +64 -0
- django_cfg/modules/django_admin/decorators/__init__.py +13 -0
- django_cfg/modules/django_admin/decorators/actions.py +106 -0
- django_cfg/modules/django_admin/decorators/display.py +106 -0
- django_cfg/modules/django_admin/mixins/__init__.py +14 -0
- django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
- django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
- django_cfg/modules/django_admin/models/__init__.py +20 -0
- django_cfg/modules/django_admin/models/action_models.py +33 -0
- django_cfg/modules/django_admin/models/badge_models.py +20 -0
- django_cfg/modules/django_admin/models/base.py +26 -0
- django_cfg/modules/django_admin/models/display_models.py +31 -0
- django_cfg/modules/django_admin/utils/badges.py +159 -0
- django_cfg/modules/django_admin/utils/displays.py +247 -0
- django_cfg/modules/django_app_agent/__init__.py +87 -0
- django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
- django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
- django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
- django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
- django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
- django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
- django_cfg/modules/django_app_agent/core/__init__.py +33 -0
- django_cfg/modules/django_app_agent/core/config.py +300 -0
- django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
- django_cfg/modules/django_app_agent/models/__init__.py +71 -0
- django_cfg/modules/django_app_agent/models/base.py +283 -0
- django_cfg/modules/django_app_agent/models/context.py +496 -0
- django_cfg/modules/django_app_agent/models/enums.py +481 -0
- django_cfg/modules/django_app_agent/models/requests.py +500 -0
- django_cfg/modules/django_app_agent/models/responses.py +585 -0
- django_cfg/modules/django_app_agent/pytest.ini +6 -0
- django_cfg/modules/django_app_agent/services/__init__.py +42 -0
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
- django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
- django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
- django_cfg/modules/django_app_agent/services/base.py +437 -0
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
- django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
- django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
- django_cfg/modules/django_app_agent/services/report_service.py +332 -0
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
- django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
- django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
- django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
- django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
- django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
- django_cfg/modules/django_app_agent/ui/cli.py +419 -0
- django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
- django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
- django_cfg/modules/django_app_agent/utils/logging.py +360 -0
- django_cfg/modules/django_app_agent/utils/validation.py +417 -0
- django_cfg/modules/django_currency/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/__init__.py +2 -2
- django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
- django_cfg/modules/django_currency/core/converter.py +12 -12
- django_cfg/modules/django_currency/database/__init__.py +2 -2
- django_cfg/modules/django_currency/database/database_loader.py +93 -42
- django_cfg/modules/django_llm/llm/client.py +10 -2
- django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
- django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
- django_cfg/modules/django_unfold/dashboard.py +14 -13
- django_cfg/modules/django_unfold/models/config.py +1 -1
- django_cfg/registry/core.py +3 -0
- django_cfg/registry/third_party.py +2 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
- django_cfg/apps/accounts/admin/activity.py +0 -96
- django_cfg/apps/accounts/admin/group.py +0 -17
- django_cfg/apps/accounts/admin/otp.py +0 -59
- django_cfg/apps/accounts/admin/registration_source.py +0 -97
- django_cfg/apps/accounts/admin/twilio_response.py +0 -227
- django_cfg/apps/accounts/admin/user.py +0 -300
- django_cfg/apps/agents/core/agent.py +0 -281
- django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
- django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
- django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
- django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
- django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
- django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
- django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
- django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
- django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
- django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
- django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
- django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
- django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
- django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
- django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
- django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
- django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
- django_cfg/apps/tasks/admin.py +0 -320
- django_cfg/middleware/static_nocache.py +0 -55
- django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
- /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
- /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.5.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"])
|