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

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