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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/inlines.py +11 -5
- django_cfg/apps/payments/admin/networks_admin.py +12 -1
- django_cfg/apps/payments/admin/payments_admin.py +13 -0
- django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
- django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
- django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
- django_cfg/apps/payments/config/__init__.py +14 -15
- django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
- django_cfg/apps/payments/config/helpers.py +8 -13
- django_cfg/apps/payments/migrations/0001_initial.py +33 -46
- django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
- django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
- django_cfg/apps/payments/models/payments.py +94 -0
- django_cfg/apps/payments/services/core/base.py +4 -4
- django_cfg/apps/payments/services/core/payment_service.py +265 -38
- django_cfg/apps/payments/services/providers/base.py +209 -3
- django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
- django_cfg/apps/payments/services/providers/models/base.py +25 -2
- django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
- django_cfg/apps/payments/services/providers/registry.py +5 -5
- django_cfg/apps/payments/services/types/requests.py +19 -7
- django_cfg/apps/payments/signals/payment_signals.py +31 -2
- django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
- django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
- django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
- django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
- django_cfg/apps/payments/urls.py +3 -2
- django_cfg/apps/payments/views/api/currencies.py +3 -0
- django_cfg/apps/payments/views/serializers/currencies.py +18 -5
- django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
- django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
- django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
- django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
- django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
- django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
- django_cfg/apps/tasks/tasks/__init__.py +10 -0
- django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
- django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
- django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
- django_cfg/apps/tasks/urls.py +2 -2
- django_cfg/apps/tasks/urls_admin.py +2 -2
- django_cfg/apps/tasks/utils/__init__.py +1 -0
- django_cfg/apps/tasks/utils/simulator.py +356 -0
- django_cfg/apps/tasks/views/__init__.py +16 -0
- django_cfg/apps/tasks/views/api.py +569 -0
- django_cfg/apps/tasks/views/dashboard.py +58 -0
- django_cfg/core/integration/__init__.py +21 -0
- django_cfg/management/commands/rundramatiq_simulator.py +430 -0
- django_cfg/models/constance.py +0 -11
- django_cfg/models/payments.py +137 -3
- django_cfg/modules/django_tasks.py +54 -21
- django_cfg/registry/core.py +4 -9
- django_cfg/template_archive/django_sample.zip +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/METADATA +2 -2
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/RECORD +85 -153
- django_cfg/apps/payments/config/constance/__init__.py +0 -22
- django_cfg/apps/payments/config/constance/config_service.py +0 -123
- django_cfg/apps/payments/config/constance/fields.py +0 -69
- django_cfg/apps/payments/config/constance/settings.py +0 -160
- django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
- django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
- django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
- django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
- django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
- django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
- django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
- django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
- django_cfg/apps/tasks/templates/tasks/base.html +0 -96
- django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
- django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
- django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
- django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
- django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
- django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
- django_cfg/apps/tasks/views.py +0 -461
- django_cfg/management/commands/app_agent_diagnose.py +0 -470
- django_cfg/management/commands/app_agent_generate.py +0 -342
- django_cfg/management/commands/app_agent_info.py +0 -308
- django_cfg/management/commands/auto_generate.py +0 -486
- django_cfg/modules/django_app_agent/__init__.py +0 -87
- django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
- django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
- django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
- django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
- django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
- django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
- django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
- django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
- django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
- django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
- django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
- django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
- django_cfg/modules/django_app_agent/core/__init__.py +0 -33
- django_cfg/modules/django_app_agent/core/config.py +0 -300
- django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
- django_cfg/modules/django_app_agent/models/__init__.py +0 -71
- django_cfg/modules/django_app_agent/models/base.py +0 -283
- django_cfg/modules/django_app_agent/models/context.py +0 -496
- django_cfg/modules/django_app_agent/models/enums.py +0 -481
- django_cfg/modules/django_app_agent/models/requests.py +0 -500
- django_cfg/modules/django_app_agent/models/responses.py +0 -585
- django_cfg/modules/django_app_agent/pytest.ini +0 -6
- django_cfg/modules/django_app_agent/services/__init__.py +0 -42
- django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
- django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
- django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
- django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
- django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
- django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
- django_cfg/modules/django_app_agent/services/base.py +0 -437
- django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
- django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
- django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
- django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
- django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
- django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
- django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
- django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
- django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
- django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
- django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
- django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
- django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
- django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
- django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
- django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
- django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
- django_cfg/modules/django_app_agent/services/report_service.py +0 -332
- django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
- django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
- django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
- django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
- django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
- django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
- django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
- django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
- django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
- django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
- django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
- django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
- django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
- django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
- django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
- django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
- django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
- django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
- django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
- django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
- django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
- django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
- django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
- django_cfg/modules/django_app_agent/ui/cli.py +0 -419
- django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
- django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
- django_cfg/modules/django_app_agent/utils/logging.py +0 -360
- django_cfg/modules/django_app_agent/utils/validation.py +0 -417
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,40 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Data Models for Context Builder Service.
|
3
|
-
|
4
|
-
This module contains Pydantic models for context building
|
5
|
-
requests, results, and related data structures.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import List, Dict, Any, Optional
|
9
|
-
from pathlib import Path
|
10
|
-
|
11
|
-
from pydantic import BaseModel, Field, ConfigDict
|
12
|
-
|
13
|
-
from ...models.context import ProjectContext, ArchitecturalPattern
|
14
|
-
from ...models.requests import AppGenerationRequest
|
15
|
-
|
16
|
-
|
17
|
-
class ContextBuildRequest(BaseModel):
|
18
|
-
"""Request for building development context."""
|
19
|
-
|
20
|
-
model_config = ConfigDict(extra='forbid', validate_assignment=True)
|
21
|
-
|
22
|
-
project_root: Path = Field(description="Root directory of the project")
|
23
|
-
target_app_name: Optional[str] = Field(default=None, description="Name of app being generated")
|
24
|
-
generation_request: Optional[AppGenerationRequest] = Field(default=None, description="App generation request")
|
25
|
-
include_code_samples: bool = Field(default=True, description="Whether to include code samples")
|
26
|
-
max_context_size: int = Field(default=50000, ge=1000, description="Maximum context size in characters")
|
27
|
-
focus_areas: List[str] = Field(default_factory=list, description="Areas to focus context on")
|
28
|
-
|
29
|
-
|
30
|
-
class ContextResult(BaseModel):
|
31
|
-
"""Result of context building operation."""
|
32
|
-
|
33
|
-
model_config = ConfigDict(extra='forbid', validate_assignment=True)
|
34
|
-
|
35
|
-
project_context: ProjectContext = Field(description="Comprehensive project context")
|
36
|
-
relevant_patterns: List[ArchitecturalPattern] = Field(default_factory=list, description="Relevant architectural patterns")
|
37
|
-
code_samples: Dict[str, str] = Field(default_factory=dict, description="Code samples for reference")
|
38
|
-
integration_points: List[Dict[str, Any]] = Field(default_factory=list, description="Potential integration points")
|
39
|
-
recommendations: List[str] = Field(default_factory=list, description="Development recommendations")
|
40
|
-
context_summary: Dict[str, Any] = Field(default_factory=dict, description="Context summary information")
|
@@ -1,85 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Pattern Analysis Module for Context Builder.
|
3
|
-
|
4
|
-
This module handles identification and analysis of architectural
|
5
|
-
patterns relevant to the development context.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import Set, List, Dict, Any
|
9
|
-
|
10
|
-
from ...models.context import ArchitecturalPattern
|
11
|
-
from ..base import ServiceDependencies
|
12
|
-
from .models import ContextBuildRequest, ContextResult
|
13
|
-
|
14
|
-
|
15
|
-
class PatternAnalyzer:
|
16
|
-
"""Handles analysis of architectural patterns for context building."""
|
17
|
-
|
18
|
-
def __init__(self):
|
19
|
-
"""Initialize pattern analyzer."""
|
20
|
-
# Pattern relevance mapping
|
21
|
-
self.pattern_relevance = {
|
22
|
-
"service_layer": {"models", "api", "business_logic"},
|
23
|
-
"repository_pattern": {"models", "data_access"},
|
24
|
-
"factory_pattern": {"models", "objects"},
|
25
|
-
"observer_pattern": {"signals", "events"},
|
26
|
-
"command_pattern": {"management_commands", "tasks"},
|
27
|
-
"mvc_pattern": {"models", "views", "urls"},
|
28
|
-
"api_pattern": {"api", "serializers", "viewsets"},
|
29
|
-
"rest_api": {"api", "serializers", "viewsets", "rest_framework"},
|
30
|
-
"graphql_api": {"api", "graphql", "schema"},
|
31
|
-
"celery_tasks": {"tasks", "background_jobs"},
|
32
|
-
"testing_pattern": {"tests", "testing"},
|
33
|
-
"django_cfg_pattern": {"config", "modules", "django_cfg"}
|
34
|
-
}
|
35
|
-
|
36
|
-
async def identify_relevant_patterns(
|
37
|
-
self,
|
38
|
-
request: ContextBuildRequest,
|
39
|
-
scan_result,
|
40
|
-
result: ContextResult,
|
41
|
-
dependencies: ServiceDependencies
|
42
|
-
) -> None:
|
43
|
-
"""Identify architectural patterns relevant to the generation request."""
|
44
|
-
all_patterns = scan_result.architectural_patterns
|
45
|
-
|
46
|
-
if not request.generation_request:
|
47
|
-
# If no specific request, include all high-confidence patterns
|
48
|
-
result.relevant_patterns = [p for p in all_patterns if p.confidence >= 0.7]
|
49
|
-
return
|
50
|
-
|
51
|
-
# Filter patterns based on requested features
|
52
|
-
relevant_patterns = []
|
53
|
-
requested_features = {f.value for f in request.generation_request.features}
|
54
|
-
|
55
|
-
for pattern in all_patterns:
|
56
|
-
# Check if pattern is relevant to requested features
|
57
|
-
if self._is_pattern_relevant(pattern, requested_features):
|
58
|
-
relevant_patterns.append(pattern)
|
59
|
-
|
60
|
-
result.relevant_patterns = relevant_patterns
|
61
|
-
|
62
|
-
def _is_pattern_relevant(self, pattern: ArchitecturalPattern, features: Set[str]) -> bool:
|
63
|
-
"""Check if architectural pattern is relevant to requested features."""
|
64
|
-
relevant_features = self.pattern_relevance.get(pattern.name, set())
|
65
|
-
return bool(relevant_features.intersection(features))
|
66
|
-
|
67
|
-
def analyze_pattern_compatibility(
|
68
|
-
self,
|
69
|
-
patterns: List[ArchitecturalPattern],
|
70
|
-
requested_features: Set[str]
|
71
|
-
) -> Dict[str, float]:
|
72
|
-
"""Analyze compatibility between patterns and requested features."""
|
73
|
-
compatibility_scores = {}
|
74
|
-
|
75
|
-
for pattern in patterns:
|
76
|
-
relevant_features = self.pattern_relevance.get(pattern.name, set())
|
77
|
-
overlap = len(relevant_features.intersection(requested_features))
|
78
|
-
total_relevant = len(relevant_features)
|
79
|
-
|
80
|
-
if total_relevant > 0:
|
81
|
-
compatibility_scores[pattern.name] = (overlap / total_relevant) * pattern.confidence
|
82
|
-
else:
|
83
|
-
compatibility_scores[pattern.name] = 0.0
|
84
|
-
|
85
|
-
return compatibility_scores
|
@@ -1,31 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Project Scanner Module for Django App Agent.
|
3
|
-
|
4
|
-
This module provides comprehensive project analysis capabilities
|
5
|
-
for Django and django-cfg projects, including app discovery,
|
6
|
-
dependency analysis, and architectural pattern detection.
|
7
|
-
|
8
|
-
Components:
|
9
|
-
- models: Data models for requests and results
|
10
|
-
- app_discovery: Django application discovery and analysis
|
11
|
-
- pattern_detection: Architectural pattern detection
|
12
|
-
- main: Main orchestration service
|
13
|
-
"""
|
14
|
-
|
15
|
-
from .main import ProjectScannerService
|
16
|
-
from .models import ProjectScanRequest, ScanResult
|
17
|
-
from .app_discovery import AppDiscoveryEngine
|
18
|
-
from .pattern_detection import PatternDetectionEngine
|
19
|
-
|
20
|
-
__all__ = [
|
21
|
-
# Main service
|
22
|
-
"ProjectScannerService",
|
23
|
-
|
24
|
-
# Data models
|
25
|
-
"ProjectScanRequest",
|
26
|
-
"ScanResult",
|
27
|
-
|
28
|
-
# Core engines
|
29
|
-
"AppDiscoveryEngine",
|
30
|
-
"PatternDetectionEngine",
|
31
|
-
]
|
@@ -1,311 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Django App Discovery Module for Project Scanner.
|
3
|
-
|
4
|
-
This module handles discovery and analysis of Django applications
|
5
|
-
within a project structure.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import List, Optional, Dict, Any
|
9
|
-
from pathlib import Path
|
10
|
-
import ast
|
11
|
-
import re
|
12
|
-
|
13
|
-
from ...models.context import DjangoAppInfo
|
14
|
-
from ...models.enums import AppType
|
15
|
-
from ..base import ServiceDependencies
|
16
|
-
from .models import ProjectScanRequest, ScanResult
|
17
|
-
|
18
|
-
|
19
|
-
class AppDiscoveryEngine:
|
20
|
-
"""Handles discovery and analysis of Django applications."""
|
21
|
-
|
22
|
-
def __init__(self):
|
23
|
-
"""Initialize app discovery engine."""
|
24
|
-
# Django file patterns
|
25
|
-
self.django_files = {
|
26
|
-
"models.py": "models",
|
27
|
-
"views.py": "views",
|
28
|
-
"urls.py": "urls",
|
29
|
-
"admin.py": "admin",
|
30
|
-
"forms.py": "forms",
|
31
|
-
"tests.py": "tests",
|
32
|
-
"apps.py": "apps",
|
33
|
-
"serializers.py": "serializers",
|
34
|
-
"signals.py": "signals",
|
35
|
-
"middleware.py": "middleware",
|
36
|
-
"management": "management_commands"
|
37
|
-
}
|
38
|
-
|
39
|
-
async def discover_django_apps(
|
40
|
-
self,
|
41
|
-
request: ProjectScanRequest,
|
42
|
-
result: ScanResult,
|
43
|
-
dependencies: ServiceDependencies
|
44
|
-
) -> None:
|
45
|
-
"""Discover Django applications in the project."""
|
46
|
-
apps_found = []
|
47
|
-
|
48
|
-
try:
|
49
|
-
# Look for Django app indicators
|
50
|
-
for path in request.project_root.rglob("apps.py"):
|
51
|
-
app_dir = path.parent
|
52
|
-
|
53
|
-
# Skip if too deep or excluded
|
54
|
-
if len(app_dir.relative_to(request.project_root).parts) > request.scan_depth:
|
55
|
-
continue
|
56
|
-
|
57
|
-
if self._is_excluded(app_dir.name, request.exclude_patterns):
|
58
|
-
continue
|
59
|
-
|
60
|
-
# Analyze app structure
|
61
|
-
app_info = await self.analyze_django_app(app_dir, request, dependencies)
|
62
|
-
if app_info:
|
63
|
-
apps_found.append(app_info)
|
64
|
-
|
65
|
-
# Also look for directories with Django files (even without apps.py)
|
66
|
-
for django_file in self.django_files.keys():
|
67
|
-
for path in request.project_root.rglob(django_file):
|
68
|
-
app_dir = path.parent
|
69
|
-
|
70
|
-
# Skip if already found or excluded
|
71
|
-
if any(app.path == str(app_dir) for app in apps_found):
|
72
|
-
continue
|
73
|
-
|
74
|
-
if self._is_excluded(app_dir.name, request.exclude_patterns):
|
75
|
-
continue
|
76
|
-
|
77
|
-
# Check if it looks like a Django app
|
78
|
-
if self._looks_like_django_app(app_dir):
|
79
|
-
app_info = await self.analyze_django_app(app_dir, request, dependencies)
|
80
|
-
if app_info:
|
81
|
-
apps_found.append(app_info)
|
82
|
-
|
83
|
-
result.discovered_apps = apps_found
|
84
|
-
|
85
|
-
except Exception as e:
|
86
|
-
dependencies.log_error("Failed to discover Django apps", e)
|
87
|
-
# Don't fail the entire scan, just log the error
|
88
|
-
|
89
|
-
async def analyze_django_app(
|
90
|
-
self,
|
91
|
-
app_dir: Path,
|
92
|
-
request: ProjectScanRequest,
|
93
|
-
dependencies: ServiceDependencies
|
94
|
-
) -> Optional[DjangoAppInfo]:
|
95
|
-
"""Analyze a single Django application."""
|
96
|
-
try:
|
97
|
-
app_name = app_dir.name
|
98
|
-
|
99
|
-
# Count Django components
|
100
|
-
models_count = self._count_models(app_dir)
|
101
|
-
views_count = self._count_views(app_dir)
|
102
|
-
urls_count = self._count_urls(app_dir)
|
103
|
-
admin_registered = self._count_admin_registrations(app_dir)
|
104
|
-
|
105
|
-
# Check for migrations
|
106
|
-
has_migrations = (app_dir / "migrations").exists() and \
|
107
|
-
any((app_dir / "migrations").glob("*.py"))
|
108
|
-
|
109
|
-
# Detect if it's a django-cfg app
|
110
|
-
is_django_cfg_app = self._is_django_cfg_app(app_dir)
|
111
|
-
|
112
|
-
# Analyze dependencies
|
113
|
-
dependencies_list = self._analyze_app_dependencies(app_dir)
|
114
|
-
|
115
|
-
return DjangoAppInfo(
|
116
|
-
name=app_name,
|
117
|
-
path=str(app_dir),
|
118
|
-
is_django_cfg_app=is_django_cfg_app,
|
119
|
-
models_count=models_count,
|
120
|
-
views_count=views_count,
|
121
|
-
urls_count=urls_count,
|
122
|
-
admin_registered_models=admin_registered,
|
123
|
-
has_migrations=has_migrations,
|
124
|
-
dependencies=dependencies_list,
|
125
|
-
description=self._extract_app_description(app_dir)
|
126
|
-
)
|
127
|
-
|
128
|
-
except Exception as e:
|
129
|
-
dependencies.log_error(f"Failed to analyze app {app_dir.name}", e)
|
130
|
-
return None
|
131
|
-
|
132
|
-
def _is_excluded(self, name: str, exclude_patterns: List[str]) -> bool:
|
133
|
-
"""Check if name matches any exclude pattern."""
|
134
|
-
for pattern in exclude_patterns:
|
135
|
-
if pattern.startswith("*") and name.endswith(pattern[1:]):
|
136
|
-
return True
|
137
|
-
elif pattern.endswith("*") and name.startswith(pattern[:-1]):
|
138
|
-
return True
|
139
|
-
elif pattern == name:
|
140
|
-
return True
|
141
|
-
return False
|
142
|
-
|
143
|
-
def _looks_like_django_app(self, app_dir: Path) -> bool:
|
144
|
-
"""Check if directory looks like a Django app."""
|
145
|
-
django_indicators = ["models.py", "views.py", "apps.py", "admin.py"]
|
146
|
-
found_indicators = sum(1 for indicator in django_indicators if (app_dir / indicator).exists())
|
147
|
-
return found_indicators >= 2 # At least 2 Django files
|
148
|
-
|
149
|
-
def _is_django_cfg_app(self, app_dir: Path) -> bool:
|
150
|
-
"""Check if app is a django-cfg app."""
|
151
|
-
# Look for django-cfg specific patterns
|
152
|
-
cfg_indicators = [
|
153
|
-
"config.py",
|
154
|
-
"cfg_config.py",
|
155
|
-
"modules/",
|
156
|
-
"__cfg__.py"
|
157
|
-
]
|
158
|
-
|
159
|
-
for indicator in cfg_indicators:
|
160
|
-
if (app_dir / indicator).exists():
|
161
|
-
return True
|
162
|
-
|
163
|
-
# Check for django-cfg imports in apps.py
|
164
|
-
apps_file = app_dir / "apps.py"
|
165
|
-
if apps_file.exists():
|
166
|
-
try:
|
167
|
-
content = apps_file.read_text()
|
168
|
-
if "django_cfg" in content or "BaseCfgModule" in content:
|
169
|
-
return True
|
170
|
-
except Exception:
|
171
|
-
pass
|
172
|
-
|
173
|
-
return False
|
174
|
-
|
175
|
-
def _count_models(self, app_dir: Path) -> int:
|
176
|
-
"""Count models in models.py."""
|
177
|
-
models_file = app_dir / "models.py"
|
178
|
-
if not models_file.exists():
|
179
|
-
return 0
|
180
|
-
|
181
|
-
try:
|
182
|
-
content = models_file.read_text()
|
183
|
-
tree = ast.parse(content)
|
184
|
-
|
185
|
-
model_count = 0
|
186
|
-
for node in ast.walk(tree):
|
187
|
-
if isinstance(node, ast.ClassDef):
|
188
|
-
# Check if it inherits from Model
|
189
|
-
for base in node.bases:
|
190
|
-
if isinstance(base, ast.Attribute) and base.attr == "Model":
|
191
|
-
model_count += 1
|
192
|
-
elif isinstance(base, ast.Name) and "Model" in base.id:
|
193
|
-
model_count += 1
|
194
|
-
|
195
|
-
return model_count
|
196
|
-
except Exception:
|
197
|
-
return 0
|
198
|
-
|
199
|
-
def _count_views(self, app_dir: Path) -> int:
|
200
|
-
"""Count views in views.py."""
|
201
|
-
views_file = app_dir / "views.py"
|
202
|
-
if not views_file.exists():
|
203
|
-
return 0
|
204
|
-
|
205
|
-
try:
|
206
|
-
content = views_file.read_text()
|
207
|
-
tree = ast.parse(content)
|
208
|
-
|
209
|
-
view_count = 0
|
210
|
-
for node in ast.walk(tree):
|
211
|
-
if isinstance(node, ast.FunctionDef):
|
212
|
-
view_count += 1
|
213
|
-
elif isinstance(node, ast.ClassDef):
|
214
|
-
# Check if it's a class-based view
|
215
|
-
for base in node.bases:
|
216
|
-
if isinstance(base, ast.Attribute) and "View" in base.attr:
|
217
|
-
view_count += 1
|
218
|
-
elif isinstance(base, ast.Name) and "View" in base.id:
|
219
|
-
view_count += 1
|
220
|
-
|
221
|
-
return view_count
|
222
|
-
except Exception:
|
223
|
-
return 0
|
224
|
-
|
225
|
-
def _count_urls(self, app_dir: Path) -> int:
|
226
|
-
"""Count URL patterns in urls.py."""
|
227
|
-
urls_file = app_dir / "urls.py"
|
228
|
-
if not urls_file.exists():
|
229
|
-
return 0
|
230
|
-
|
231
|
-
try:
|
232
|
-
content = urls_file.read_text()
|
233
|
-
# Count path() and url() calls
|
234
|
-
path_count = content.count("path(")
|
235
|
-
url_count = content.count("url(")
|
236
|
-
return path_count + url_count
|
237
|
-
except Exception:
|
238
|
-
return 0
|
239
|
-
|
240
|
-
def _count_admin_registrations(self, app_dir: Path) -> int:
|
241
|
-
"""Count admin registrations in admin.py."""
|
242
|
-
admin_file = app_dir / "admin.py"
|
243
|
-
if not admin_file.exists():
|
244
|
-
return 0
|
245
|
-
|
246
|
-
try:
|
247
|
-
content = admin_file.read_text()
|
248
|
-
# Count admin.register calls and @admin.register decorators
|
249
|
-
register_count = content.count("admin.register(")
|
250
|
-
decorator_count = content.count("@admin.register")
|
251
|
-
return register_count + decorator_count
|
252
|
-
except Exception:
|
253
|
-
return 0
|
254
|
-
|
255
|
-
def _analyze_app_dependencies(self, app_dir: Path) -> List[str]:
|
256
|
-
"""Analyze app dependencies from imports."""
|
257
|
-
dependencies = set()
|
258
|
-
|
259
|
-
# Analyze Python files for imports
|
260
|
-
for py_file in app_dir.rglob("*.py"):
|
261
|
-
try:
|
262
|
-
content = py_file.read_text()
|
263
|
-
tree = ast.parse(content)
|
264
|
-
|
265
|
-
for node in ast.walk(tree):
|
266
|
-
if isinstance(node, ast.Import):
|
267
|
-
for alias in node.names:
|
268
|
-
if "." in alias.name:
|
269
|
-
dependencies.add(alias.name.split(".")[0])
|
270
|
-
elif isinstance(node, ast.ImportFrom):
|
271
|
-
if node.module:
|
272
|
-
if "." in node.module:
|
273
|
-
dependencies.add(node.module.split(".")[0])
|
274
|
-
else:
|
275
|
-
dependencies.add(node.module)
|
276
|
-
except Exception:
|
277
|
-
continue
|
278
|
-
|
279
|
-
# Filter to keep only relevant dependencies
|
280
|
-
relevant_deps = []
|
281
|
-
for dep in dependencies:
|
282
|
-
if dep in ["django", "rest_framework", "django_cfg"] or dep.startswith("django_"):
|
283
|
-
relevant_deps.append(dep)
|
284
|
-
|
285
|
-
return relevant_deps
|
286
|
-
|
287
|
-
def _extract_app_description(self, app_dir: Path) -> Optional[str]:
|
288
|
-
"""Extract app description from apps.py or docstrings."""
|
289
|
-
apps_file = app_dir / "apps.py"
|
290
|
-
if apps_file.exists():
|
291
|
-
try:
|
292
|
-
content = apps_file.read_text()
|
293
|
-
tree = ast.parse(content)
|
294
|
-
|
295
|
-
for node in ast.walk(tree):
|
296
|
-
if isinstance(node, ast.ClassDef):
|
297
|
-
# Look for verbose_name in Config class
|
298
|
-
for item in node.body:
|
299
|
-
if isinstance(item, ast.Assign):
|
300
|
-
for target in item.targets:
|
301
|
-
if isinstance(target, ast.Name) and target.id == "verbose_name":
|
302
|
-
if isinstance(item.value, ast.Constant):
|
303
|
-
return item.value.value
|
304
|
-
|
305
|
-
# Use class docstring as fallback
|
306
|
-
if ast.get_docstring(node):
|
307
|
-
return ast.get_docstring(node)
|
308
|
-
except Exception:
|
309
|
-
pass
|
310
|
-
|
311
|
-
return None
|
@@ -1,221 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Main Project Scanner Service for Django App Agent Module.
|
3
|
-
|
4
|
-
This service provides comprehensive project structure analysis,
|
5
|
-
coordinating app discovery, dependency analysis, and pattern detection.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import Dict, Any
|
9
|
-
from pathlib import Path
|
10
|
-
|
11
|
-
from ..base import BaseService, ServiceDependencies
|
12
|
-
from ...models.context import ProjectContext
|
13
|
-
from ...core.exceptions import FileSystemError
|
14
|
-
|
15
|
-
from .models import ProjectScanRequest, ScanResult
|
16
|
-
from .app_discovery import AppDiscoveryEngine
|
17
|
-
from .pattern_detection import PatternDetectionEngine
|
18
|
-
|
19
|
-
|
20
|
-
class ProjectScannerService(BaseService[ProjectScanRequest, ScanResult]):
|
21
|
-
"""
|
22
|
-
Service for comprehensive Django/django-cfg project analysis.
|
23
|
-
|
24
|
-
Capabilities:
|
25
|
-
- Discover Django applications and their structure
|
26
|
-
- Analyze project dependencies and relationships
|
27
|
-
- Detect architectural patterns and conventions
|
28
|
-
- Generate comprehensive project context
|
29
|
-
- Identify integration opportunities
|
30
|
-
"""
|
31
|
-
|
32
|
-
def __init__(self, config):
|
33
|
-
"""Initialize project scanner service."""
|
34
|
-
super().__init__("project_scanner", config)
|
35
|
-
|
36
|
-
# Initialize sub-engines
|
37
|
-
self.app_discovery = AppDiscoveryEngine()
|
38
|
-
self.pattern_detection = PatternDetectionEngine()
|
39
|
-
|
40
|
-
async def process(
|
41
|
-
self,
|
42
|
-
request: ProjectScanRequest,
|
43
|
-
dependencies: ServiceDependencies
|
44
|
-
) -> ScanResult:
|
45
|
-
"""
|
46
|
-
Process project scanning request.
|
47
|
-
|
48
|
-
Args:
|
49
|
-
request: Project scanning request
|
50
|
-
dependencies: Service dependencies for logging and operations
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
ScanResult with comprehensive project analysis
|
54
|
-
"""
|
55
|
-
try:
|
56
|
-
dependencies.log_operation(
|
57
|
-
f"Starting project scan for '{request.project_root}'",
|
58
|
-
project_root=str(request.project_root),
|
59
|
-
scan_depth=request.scan_depth
|
60
|
-
)
|
61
|
-
|
62
|
-
# Initialize result with basic project context
|
63
|
-
result = ScanResult(
|
64
|
-
project_context=ProjectContext(
|
65
|
-
project_root=str(request.project_root),
|
66
|
-
project_name=request.project_root.name,
|
67
|
-
django_version="Unknown",
|
68
|
-
is_django_cfg_project=False,
|
69
|
-
apps=[],
|
70
|
-
architectural_patterns=[],
|
71
|
-
integration_opportunities=[]
|
72
|
-
)
|
73
|
-
)
|
74
|
-
|
75
|
-
# Scan project structure
|
76
|
-
await self._scan_project_structure(request, result, dependencies)
|
77
|
-
|
78
|
-
# Discover Django applications
|
79
|
-
await self.app_discovery.discover_django_apps(request, result, dependencies)
|
80
|
-
|
81
|
-
# Analyze dependencies if requested
|
82
|
-
if request.analyze_dependencies:
|
83
|
-
await self._analyze_dependencies(request, result, dependencies)
|
84
|
-
|
85
|
-
# Detect architectural patterns if requested
|
86
|
-
if request.detect_patterns:
|
87
|
-
await self.pattern_detection.detect_patterns(request, result, dependencies)
|
88
|
-
|
89
|
-
# Update project context with findings
|
90
|
-
self._update_project_context(result)
|
91
|
-
|
92
|
-
dependencies.log_operation(
|
93
|
-
"Project scan completed successfully",
|
94
|
-
apps_found=len(result.discovered_apps),
|
95
|
-
patterns_detected=len(result.architectural_patterns)
|
96
|
-
)
|
97
|
-
|
98
|
-
return result
|
99
|
-
|
100
|
-
except Exception as e:
|
101
|
-
raise FileSystemError(
|
102
|
-
f"Failed to scan project structure: {e}",
|
103
|
-
file_path=str(request.project_root),
|
104
|
-
operation="scan_project"
|
105
|
-
)
|
106
|
-
|
107
|
-
async def _scan_project_structure(
|
108
|
-
self,
|
109
|
-
request: ProjectScanRequest,
|
110
|
-
result: ScanResult,
|
111
|
-
dependencies: ServiceDependencies
|
112
|
-
) -> None:
|
113
|
-
"""Scan basic project structure and collect file statistics."""
|
114
|
-
try:
|
115
|
-
file_counts = {}
|
116
|
-
total_files = 0
|
117
|
-
total_directories = 0
|
118
|
-
directories_scanned = 0
|
119
|
-
|
120
|
-
# Walk through project directory
|
121
|
-
for path in request.project_root.rglob("*"):
|
122
|
-
# Check depth limit
|
123
|
-
relative_path = path.relative_to(request.project_root)
|
124
|
-
if len(relative_path.parts) > request.scan_depth:
|
125
|
-
continue
|
126
|
-
|
127
|
-
# Check exclusion patterns
|
128
|
-
if self._is_excluded(path.name, request.exclude_patterns):
|
129
|
-
continue
|
130
|
-
|
131
|
-
if path.is_file():
|
132
|
-
total_files += 1
|
133
|
-
suffix = path.suffix.lower()
|
134
|
-
file_counts[suffix] = file_counts.get(suffix, 0) + 1
|
135
|
-
elif path.is_dir():
|
136
|
-
total_directories += 1
|
137
|
-
directories_scanned += 1
|
138
|
-
|
139
|
-
# Store file summary
|
140
|
-
result.file_summary = {
|
141
|
-
"total_files": total_files,
|
142
|
-
"total_directories": total_directories,
|
143
|
-
"file_types": file_counts,
|
144
|
-
"python_files": file_counts.get(".py", 0),
|
145
|
-
"template_files": file_counts.get(".html", 0) + file_counts.get(".htm", 0),
|
146
|
-
"static_files": file_counts.get(".css", 0) + file_counts.get(".js", 0) + file_counts.get(".scss", 0),
|
147
|
-
"config_files": file_counts.get(".yml", 0) + file_counts.get(".yaml", 0) + file_counts.get(".json", 0)
|
148
|
-
}
|
149
|
-
|
150
|
-
# Store scan statistics
|
151
|
-
result.scan_statistics = {
|
152
|
-
"files_scanned": total_files,
|
153
|
-
"directories_scanned": directories_scanned,
|
154
|
-
"scan_depth": request.scan_depth
|
155
|
-
}
|
156
|
-
|
157
|
-
except Exception as e:
|
158
|
-
raise FileSystemError(
|
159
|
-
f"Failed to scan project structure: {e}",
|
160
|
-
file_path=str(request.project_root),
|
161
|
-
operation="scan_project"
|
162
|
-
)
|
163
|
-
|
164
|
-
async def _analyze_dependencies(
|
165
|
-
self,
|
166
|
-
request: ProjectScanRequest,
|
167
|
-
result: ScanResult,
|
168
|
-
dependencies: ServiceDependencies
|
169
|
-
) -> None:
|
170
|
-
"""Analyze dependencies between discovered apps."""
|
171
|
-
try:
|
172
|
-
dependency_graph = {}
|
173
|
-
|
174
|
-
for app in result.discovered_apps:
|
175
|
-
app_deps = []
|
176
|
-
|
177
|
-
# Find dependencies to other discovered apps
|
178
|
-
for other_app in result.discovered_apps:
|
179
|
-
if app.name != other_app.name:
|
180
|
-
# Check if app imports from other_app
|
181
|
-
if other_app.name in app.dependencies:
|
182
|
-
app_deps.append(other_app.name)
|
183
|
-
|
184
|
-
dependency_graph[app.name] = app_deps
|
185
|
-
|
186
|
-
result.dependency_graph = dependency_graph
|
187
|
-
|
188
|
-
except Exception as e:
|
189
|
-
dependencies.log_error("Failed to analyze dependencies", e)
|
190
|
-
|
191
|
-
def _update_project_context(self, result: ScanResult) -> None:
|
192
|
-
"""Update project context with scan results."""
|
193
|
-
# Check if it's a django-cfg project
|
194
|
-
is_django_cfg = any(app.is_django_cfg_app for app in result.discovered_apps)
|
195
|
-
|
196
|
-
# Detect Django version (placeholder)
|
197
|
-
django_version = self.pattern_detection.detect_django_version(result.discovered_apps)
|
198
|
-
|
199
|
-
# Update project context
|
200
|
-
result.project_context.django_version = django_version
|
201
|
-
result.project_context.is_django_cfg_project = is_django_cfg
|
202
|
-
result.project_context.apps = result.discovered_apps
|
203
|
-
result.project_context.architectural_patterns = result.architectural_patterns
|
204
|
-
|
205
|
-
# Generate integration opportunities (placeholder)
|
206
|
-
result.project_context.integration_opportunities = [
|
207
|
-
"Consider adding API endpoints for existing models",
|
208
|
-
"Implement caching for frequently accessed data",
|
209
|
-
"Add comprehensive test coverage"
|
210
|
-
]
|
211
|
-
|
212
|
-
def _is_excluded(self, name: str, exclude_patterns: list[str]) -> bool:
|
213
|
-
"""Check if name matches any exclude pattern."""
|
214
|
-
for pattern in exclude_patterns:
|
215
|
-
if pattern.startswith("*") and name.endswith(pattern[1:]):
|
216
|
-
return True
|
217
|
-
elif pattern.endswith("*") and name.startswith(pattern[:-1]):
|
218
|
-
return True
|
219
|
-
elif pattern == name:
|
220
|
-
return True
|
221
|
-
return False
|