django-cfg 1.5.1__py3-none-any.whl → 1.5.2__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (121) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
  3. django_cfg/apps/dashboard/serializers/__init__.py +0 -12
  4. django_cfg/apps/dashboard/serializers/activity.py +1 -1
  5. django_cfg/apps/dashboard/services/__init__.py +0 -2
  6. django_cfg/apps/dashboard/services/charts_service.py +4 -3
  7. django_cfg/apps/dashboard/services/statistics_service.py +11 -2
  8. django_cfg/apps/dashboard/services/system_health_service.py +64 -106
  9. django_cfg/apps/dashboard/urls.py +0 -2
  10. django_cfg/apps/dashboard/views/__init__.py +0 -2
  11. django_cfg/apps/dashboard/views/commands_views.py +3 -6
  12. django_cfg/apps/dashboard/views/overview_views.py +14 -13
  13. django_cfg/apps/knowbase/apps.py +2 -2
  14. django_cfg/apps/maintenance/admin/api_key_admin.py +2 -3
  15. django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
  16. django_cfg/apps/rq/__init__.py +9 -0
  17. django_cfg/apps/rq/apps.py +80 -0
  18. django_cfg/apps/rq/management/__init__.py +1 -0
  19. django_cfg/apps/rq/management/commands/__init__.py +1 -0
  20. django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
  21. django_cfg/apps/rq/management/commands/rqstats.py +33 -0
  22. django_cfg/apps/rq/management/commands/rqworker.py +31 -0
  23. django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
  24. django_cfg/apps/rq/serializers/__init__.py +40 -0
  25. django_cfg/apps/rq/serializers/health.py +60 -0
  26. django_cfg/apps/rq/serializers/job.py +100 -0
  27. django_cfg/apps/rq/serializers/queue.py +80 -0
  28. django_cfg/apps/rq/serializers/schedule.py +178 -0
  29. django_cfg/apps/rq/serializers/testing.py +139 -0
  30. django_cfg/apps/rq/serializers/worker.py +58 -0
  31. django_cfg/apps/rq/services/__init__.py +25 -0
  32. django_cfg/apps/rq/services/config_helper.py +233 -0
  33. django_cfg/apps/rq/services/models/README.md +417 -0
  34. django_cfg/apps/rq/services/models/__init__.py +30 -0
  35. django_cfg/apps/rq/services/models/event.py +123 -0
  36. django_cfg/apps/rq/services/models/job.py +99 -0
  37. django_cfg/apps/rq/services/models/queue.py +92 -0
  38. django_cfg/apps/rq/services/models/worker.py +104 -0
  39. django_cfg/apps/rq/services/rq_converters.py +183 -0
  40. django_cfg/apps/rq/tasks/__init__.py +23 -0
  41. django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
  42. django_cfg/apps/rq/urls.py +54 -0
  43. django_cfg/apps/rq/views/__init__.py +19 -0
  44. django_cfg/apps/rq/views/jobs.py +882 -0
  45. django_cfg/apps/rq/views/monitoring.py +248 -0
  46. django_cfg/apps/rq/views/queues.py +261 -0
  47. django_cfg/apps/rq/views/schedule.py +400 -0
  48. django_cfg/apps/rq/views/testing.py +761 -0
  49. django_cfg/apps/rq/views/workers.py +195 -0
  50. django_cfg/apps/urls.py +6 -7
  51. django_cfg/core/base/config_model.py +10 -26
  52. django_cfg/core/builders/apps_builder.py +4 -11
  53. django_cfg/core/generation/integration_generators/__init__.py +3 -6
  54. django_cfg/core/generation/integration_generators/django_rq.py +80 -0
  55. django_cfg/core/generation/orchestrator.py +9 -19
  56. django_cfg/core/integration/display/startup.py +6 -20
  57. django_cfg/mixins/__init__.py +2 -0
  58. django_cfg/mixins/superadmin_api.py +59 -0
  59. django_cfg/models/__init__.py +3 -3
  60. django_cfg/models/django/__init__.py +3 -3
  61. django_cfg/models/django/django_rq.py +621 -0
  62. django_cfg/models/django/revolution_legacy.py +1 -1
  63. django_cfg/modules/base.py +4 -6
  64. django_cfg/modules/django_admin/config/background_task_config.py +4 -4
  65. django_cfg/modules/django_admin/utils/html/composition.py +9 -2
  66. django_cfg/modules/django_unfold/navigation.py +1 -26
  67. django_cfg/pyproject.toml +4 -4
  68. django_cfg/registry/core.py +4 -7
  69. django_cfg/static/frontend/admin.zip +0 -0
  70. django_cfg/templates/admin/constance/includes/results_list.html +73 -0
  71. django_cfg/templates/admin/index.html +187 -62
  72. django_cfg/templatetags/django_cfg.py +61 -1
  73. {django_cfg-1.5.1.dist-info → django_cfg-1.5.2.dist-info}/METADATA +5 -6
  74. {django_cfg-1.5.1.dist-info → django_cfg-1.5.2.dist-info}/RECORD +77 -82
  75. django_cfg/apps/dashboard/permissions.py +0 -48
  76. django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
  77. django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
  78. django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
  79. django_cfg/apps/tasks/__init__.py +0 -64
  80. django_cfg/apps/tasks/admin/__init__.py +0 -4
  81. django_cfg/apps/tasks/admin/config.py +0 -98
  82. django_cfg/apps/tasks/admin/task_log.py +0 -238
  83. django_cfg/apps/tasks/apps.py +0 -15
  84. django_cfg/apps/tasks/filters/__init__.py +0 -10
  85. django_cfg/apps/tasks/filters/task_log.py +0 -121
  86. django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
  87. django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
  88. django_cfg/apps/tasks/migrations/__init__.py +0 -0
  89. django_cfg/apps/tasks/models/__init__.py +0 -4
  90. django_cfg/apps/tasks/models/task_log.py +0 -246
  91. django_cfg/apps/tasks/serializers/__init__.py +0 -28
  92. django_cfg/apps/tasks/serializers/task_log.py +0 -249
  93. django_cfg/apps/tasks/services/__init__.py +0 -10
  94. django_cfg/apps/tasks/services/client/__init__.py +0 -7
  95. django_cfg/apps/tasks/services/client/client.py +0 -234
  96. django_cfg/apps/tasks/services/config_helper.py +0 -63
  97. django_cfg/apps/tasks/services/sync.py +0 -204
  98. django_cfg/apps/tasks/urls.py +0 -16
  99. django_cfg/apps/tasks/views/__init__.py +0 -10
  100. django_cfg/apps/tasks/views/task_log.py +0 -41
  101. django_cfg/apps/tasks/views/task_log_base.py +0 -41
  102. django_cfg/apps/tasks/views/task_log_overview.py +0 -100
  103. django_cfg/apps/tasks/views/task_log_related.py +0 -41
  104. django_cfg/apps/tasks/views/task_log_stats.py +0 -91
  105. django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
  106. django_cfg/core/generation/integration_generators/django_q2.py +0 -133
  107. django_cfg/core/generation/integration_generators/tasks.py +0 -88
  108. django_cfg/models/django/django_q2.py +0 -514
  109. django_cfg/models/tasks/__init__.py +0 -49
  110. django_cfg/models/tasks/backends.py +0 -122
  111. django_cfg/models/tasks/config.py +0 -209
  112. django_cfg/models/tasks/utils.py +0 -162
  113. django_cfg/modules/django_q2/README.md +0 -140
  114. django_cfg/modules/django_q2/__init__.py +0 -8
  115. django_cfg/modules/django_q2/apps.py +0 -107
  116. django_cfg/modules/django_q2/management/__init__.py +0 -0
  117. django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
  118. django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
  119. {django_cfg-1.5.1.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
  120. {django_cfg-1.5.1.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
  121. {django_cfg-1.5.1.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,195 @@
1
+ """
2
+ Django-RQ Worker Management ViewSet.
3
+
4
+ Provides REST API endpoints for monitoring RQ workers.
5
+ """
6
+
7
+ from django_cfg.mixins import AdminAPIMixin
8
+ from django_cfg.modules.django_logging import get_logger
9
+ from drf_spectacular.utils import extend_schema, OpenApiParameter
10
+ from rest_framework import status, viewsets
11
+ from rest_framework.decorators import action
12
+ from rest_framework.response import Response
13
+
14
+ from ..serializers import WorkerSerializer, WorkerStatsSerializer
15
+ from ..services import worker_to_model
16
+
17
+ logger = get_logger("rq.workers")
18
+
19
+
20
+ class WorkerViewSet(AdminAPIMixin, viewsets.ViewSet):
21
+ """
22
+ ViewSet for RQ worker monitoring.
23
+
24
+ Provides endpoints for:
25
+ - Listing all workers with statistics
26
+ - Getting worker aggregated stats
27
+ - Getting individual worker details
28
+
29
+ Requires admin authentication (JWT, Session, or Basic Auth).
30
+ """
31
+
32
+ @extend_schema(
33
+ tags=["RQ Workers"],
34
+ summary="List all workers",
35
+ description="Returns list of all RQ workers with their current state. Supports filtering by state and queue.",
36
+ parameters=[
37
+ OpenApiParameter(
38
+ name="state",
39
+ type=str,
40
+ location=OpenApiParameter.QUERY,
41
+ required=False,
42
+ description="Filter by worker state (idle, busy, suspended)",
43
+ ),
44
+ OpenApiParameter(
45
+ name="queue",
46
+ type=str,
47
+ location=OpenApiParameter.QUERY,
48
+ required=False,
49
+ description="Filter by queue name",
50
+ ),
51
+ ],
52
+ responses={
53
+ 200: WorkerSerializer(many=True),
54
+ },
55
+ )
56
+ def list(self, request):
57
+ """List all workers with optional filtering."""
58
+ try:
59
+ import django_rq
60
+ from rq import Worker
61
+
62
+ # Get query params for filtering
63
+ state_filter = request.query_params.get('state')
64
+ queue_filter = request.query_params.get('queue')
65
+
66
+ # Get all workers using connection from default queue
67
+ # All queues share the same Redis connection, so we only need one
68
+ queue = django_rq.get_queue('default')
69
+ all_workers = Worker.all(connection=queue.connection)
70
+
71
+ workers_data = []
72
+ for worker in all_workers:
73
+ try:
74
+ # Convert RQ Worker to Pydantic model
75
+ worker_model = worker_to_model(worker)
76
+
77
+ # Apply filters
78
+ if state_filter and worker_model.state != state_filter:
79
+ continue
80
+
81
+ if queue_filter and queue_filter not in worker_model.get_queue_list():
82
+ continue
83
+
84
+ # Convert Pydantic model to dict for DRF serializer
85
+ worker_dict = worker_model.model_dump()
86
+
87
+ # DRF serializer expects 'queues' as list, not comma-separated string
88
+ worker_dict['queues'] = worker_model.get_queue_list()
89
+
90
+ # DRF serializer expects 'current_job' not 'current_job_id'
91
+ worker_dict['current_job'] = worker_dict.pop('current_job_id')
92
+
93
+ serializer = WorkerSerializer(data=worker_dict)
94
+ serializer.is_valid(raise_exception=True)
95
+ workers_data.append(serializer.data)
96
+
97
+ except Exception as e:
98
+ logger.debug(f"Failed to get worker {worker.name}: {e}")
99
+ continue
100
+
101
+ return Response(workers_data)
102
+
103
+ except Exception as e:
104
+ import traceback
105
+ logger.error(f"Worker list error: {e}", exc_info=True)
106
+ return Response(
107
+ {
108
+ "error": str(e),
109
+ "traceback": traceback.format_exc(),
110
+ },
111
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
112
+ )
113
+
114
+ @extend_schema(
115
+ tags=["RQ Workers"],
116
+ summary="Get worker statistics",
117
+ description="Returns aggregated statistics for all workers.",
118
+ responses={
119
+ 200: WorkerStatsSerializer,
120
+ },
121
+ )
122
+ @action(detail=False, methods=["get"], url_path="stats")
123
+ def stats(self, request):
124
+ """Get aggregated worker statistics."""
125
+ try:
126
+ import django_rq
127
+ from rq import Worker
128
+
129
+ # Get all workers using connection from default queue
130
+ # All queues share the same Redis connection, so we only need one
131
+ queue = django_rq.get_queue('default')
132
+ all_workers = Worker.all(connection=queue.connection)
133
+
134
+ # Count by state
135
+ busy_workers = 0
136
+ idle_workers = 0
137
+ suspended_workers = 0
138
+ total_successful = 0
139
+ total_failed = 0
140
+ total_working_time = 0.0
141
+
142
+ workers_list = []
143
+
144
+ for worker in all_workers:
145
+ try:
146
+ # Convert RQ Worker to Pydantic model
147
+ worker_model = worker_to_model(worker)
148
+
149
+ # Count by state
150
+ if worker_model.is_busy:
151
+ busy_workers += 1
152
+ elif worker_model.is_idle:
153
+ idle_workers += 1
154
+ elif worker_model.state == 'suspended':
155
+ suspended_workers += 1
156
+
157
+ total_successful += worker_model.successful_job_count
158
+ total_failed += worker_model.failed_job_count
159
+ total_working_time += worker_model.total_working_time
160
+
161
+ # Convert to dict for serializer
162
+ worker_dict = worker_model.model_dump()
163
+ worker_dict['queues'] = worker_model.get_queue_list()
164
+ worker_dict['current_job'] = worker_dict.pop('current_job_id')
165
+ workers_list.append(worker_dict)
166
+
167
+ except Exception as e:
168
+ logger.debug(f"Failed to process worker: {e}")
169
+ continue
170
+
171
+ stats_data = {
172
+ "total_workers": len(all_workers),
173
+ "busy_workers": busy_workers,
174
+ "idle_workers": idle_workers,
175
+ "suspended_workers": suspended_workers,
176
+ "total_successful_jobs": total_successful,
177
+ "total_failed_jobs": total_failed,
178
+ "total_working_time": total_working_time,
179
+ "workers": workers_list,
180
+ }
181
+
182
+ serializer = WorkerStatsSerializer(data=stats_data)
183
+ serializer.is_valid(raise_exception=True)
184
+ return Response(serializer.data)
185
+
186
+ except Exception as e:
187
+ import traceback
188
+ logger.error(f"Worker stats error: {e}", exc_info=True)
189
+ return Response(
190
+ {
191
+ "error": str(e),
192
+ "traceback": traceback.format_exc(),
193
+ },
194
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
195
+ )
django_cfg/apps/urls.py CHANGED
@@ -44,15 +44,15 @@ def get_enabled_cfg_apps() -> List[str]:
44
44
  if base_module.is_agents_enabled():
45
45
  enabled_apps.append("django_cfg.apps.agents")
46
46
 
47
- if base_module.should_enable_tasks():
48
- enabled_apps.append("django_cfg.apps.tasks")
49
-
50
47
  if base_module.is_payments_enabled():
51
48
  enabled_apps.append("django_cfg.apps.payments")
52
49
 
53
50
  if base_module.is_centrifugo_enabled():
54
51
  enabled_apps.append("django_cfg.apps.centrifugo")
55
52
 
53
+ if base_module.should_enable_rq():
54
+ enabled_apps.append("django_cfg.apps.rq")
55
+
56
56
  if base_module.is_grpc_enabled():
57
57
  enabled_apps.append("django_cfg.apps.grpc")
58
58
 
@@ -176,16 +176,15 @@ APP_URL_MAP = {
176
176
  "django_cfg.apps.agents": [
177
177
  ("cfg/agents/", "django_cfg.apps.agents.urls"),
178
178
  ],
179
- "django_cfg.apps.tasks": [
180
- ("cfg/tasks/", "django_cfg.apps.tasks.urls"),
181
- ],
182
179
  "django_cfg.apps.payments": [
183
180
  ("cfg/payments/", "django_cfg.apps.payments.urls"),
184
- # Payments v2.0: No separate urls_admin (uses Django Admin only)
185
181
  ],
186
182
  "django_cfg.apps.centrifugo": [
187
183
  ("cfg/centrifugo/", "django_cfg.apps.centrifugo.urls"),
188
184
  ],
185
+ "django_cfg.apps.rq": [
186
+ ("cfg/rq/", "django_cfg.apps.rq.urls"),
187
+ ],
189
188
  "django_cfg.apps.grpc": [
190
189
  ("cfg/grpc/", "django_cfg.apps.grpc.urls"),
191
190
  ],
@@ -20,9 +20,9 @@ from ...models import (
20
20
  ApiKeys,
21
21
  AxesConfig,
22
22
  CacheConfig,
23
- DjangoQ2Config,
24
23
  CryptoFieldsConfig,
25
24
  DatabaseConfig,
25
+ DjangoRQConfig,
26
26
  DRFConfig,
27
27
  EmailConfig,
28
28
  LimitsConfig,
@@ -33,7 +33,6 @@ from ...models import (
33
33
  from ...models.api.grpc import GRPCConfig
34
34
  from ...models.ngrok import NgrokConfig
35
35
  from ...models.payments import PaymentsConfig
36
- from ...models.tasks import TaskConfig
37
36
  from ...modules.nextjs_admin import NextJsAdminConfig
38
37
  from ..exceptions import ConfigurationError
39
38
  from ..types.enums import EnvironmentMode, StartupInfoMode
@@ -224,7 +223,7 @@ class DjangoConfig(BaseModel):
224
223
  description=(
225
224
  "Redis connection URL (redis://host:port/db). "
226
225
  "If set and cache_default is None, automatically creates RedisCache backend. "
227
- "Also used by tasks (ReArq, Django-Q2) if they don't specify broker_url."
226
+ "Also used by Django-RQ if queues don't specify connection details."
228
227
  ),
229
228
  )
230
229
 
@@ -324,16 +323,10 @@ class DjangoConfig(BaseModel):
324
323
  description="Enable modern Tailwind CSS theme for Django REST Framework Browsable API",
325
324
  )
326
325
 
327
- # === Background Task Processing ===
328
- tasks: Optional[TaskConfig] = Field(
326
+ # === Django-RQ Task Queue & Scheduler ===
327
+ django_rq: Optional[DjangoRQConfig] = Field(
329
328
  default=None,
330
- description="Background task processing configuration (ReArq)",
331
- )
332
-
333
- # === Django-Q2 Task Scheduling ===
334
- django_q2: Optional[DjangoQ2Config] = Field(
335
- default=None,
336
- description="Django-Q2 task scheduling and queue configuration",
329
+ description="Django-RQ task queue and scheduler configuration (sync tasks, cron scheduling)",
337
330
  )
338
331
 
339
332
  # === Centrifugo Configuration ===
@@ -641,28 +634,19 @@ class DjangoConfig(BaseModel):
641
634
  **kwargs
642
635
  )
643
636
 
644
- def should_enable_tasks(self) -> bool:
637
+ def should_enable_rq(self) -> bool:
645
638
  """
646
- Determine if background tasks should be enabled.
639
+ Determine if Django-RQ should be enabled.
647
640
 
648
- Tasks are enabled if:
649
- 1. Explicitly configured via tasks field
650
- 2. Knowledge base is enabled (requires background processing)
651
- 3. Agents are enabled (requires background processing)
641
+ Django-RQ is enabled if explicitly configured via django_rq field.
652
642
 
653
643
  Returns:
654
- True if tasks should be enabled, False otherwise
644
+ True if Django-RQ should be enabled, False otherwise
655
645
  """
656
- # Check if explicitly configured
657
- if hasattr(self, 'tasks') and self.tasks and self.tasks.enabled:
658
- return True
659
-
660
- # Check if features that require tasks are enabled
661
- if self.enable_knowbase or self.enable_agents:
646
+ if hasattr(self, 'django_rq') and self.django_rq and self.django_rq.enabled:
662
647
  return True
663
648
 
664
649
  return False
665
650
 
666
-
667
651
  # Export main class
668
652
  __all__ = ["DjangoConfig"]
@@ -23,7 +23,6 @@ class InstalledAppsBuilder:
23
23
  - Combine default Django/third-party apps
24
24
  - Add django-cfg apps based on enabled features
25
25
  - Handle special ordering (accounts before admin)
26
- - Auto-enable tasks if needed
27
26
  - Auto-detect dashboard apps from Unfold
28
27
  - Add project-specific apps
29
28
  - Remove duplicates while preserving order
@@ -161,16 +160,10 @@ class InstalledAppsBuilder:
161
160
  """
162
161
  apps = []
163
162
 
164
- # Auto-enable tasks if needed by other features
165
- if self.config.should_enable_tasks():
166
- # No external app needed - ReArq is embedded
167
- apps.append("django_cfg.apps.tasks")
168
-
169
- # Add django-q2 if enabled
170
- if hasattr(self.config, "django_q2") and self.config.django_q2 and self.config.django_q2.enabled:
171
- apps.append("django_q")
172
- # Auto-add django_q2 module for automatic schedule synchronization
173
- apps.append("django_cfg.modules.django_q2")
163
+ # Add Django-RQ if enabled
164
+ if hasattr(self.config, "django_rq") and self.config.django_rq and self.config.django_rq.enabled:
165
+ apps.append("django_rq") # Core django-rq package
166
+ apps.append("django_cfg.apps.rq") # Django-CFG monitoring & API
174
167
 
175
168
  # Add DRF Tailwind theme module (uses Tailwind via CDN)
176
169
  if self.config.enable_drf_tailwind:
@@ -5,21 +5,18 @@ Contains generators for third-party integrations and frameworks:
5
5
  - Session configuration
6
6
  - External services (Telegram, Unfold, Constance)
7
7
  - API frameworks (JWT, DRF, Spectacular, OpenAPI Client)
8
- - Background tasks (ReArq)
9
- - Task scheduling (django-q2)
8
+ - Task scheduling (django-rq)
10
9
  - Tailwind CSS configuration
11
10
  """
12
11
 
13
12
  from .api import APIFrameworksGenerator
14
- from .django_q2 import DjangoQ2SettingsGenerator
13
+ from .django_rq import DjangoRQSettingsGenerator
15
14
  from .sessions import SessionSettingsGenerator
16
- from .tasks import TasksSettingsGenerator
17
15
  from .third_party import ThirdPartyIntegrationsGenerator
18
16
 
19
17
  __all__ = [
20
18
  "SessionSettingsGenerator",
21
19
  "ThirdPartyIntegrationsGenerator",
22
20
  "APIFrameworksGenerator",
23
- "TasksSettingsGenerator",
24
- "DjangoQ2SettingsGenerator",
21
+ "DjangoRQSettingsGenerator",
25
22
  ]
@@ -0,0 +1,80 @@
1
+ """
2
+ Django-RQ Settings Generator.
3
+
4
+ Generates Django settings for django-rq task queue and scheduler.
5
+ Converts DjangoRQConfig Pydantic model to Django RQ_QUEUES and related settings.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Any, Dict, Optional
9
+
10
+ from ....models.django.django_rq import DjangoRQConfig
11
+
12
+ if TYPE_CHECKING:
13
+ from ...base.config_model import DjangoConfig
14
+
15
+
16
+ class DjangoRQSettingsGenerator:
17
+ """
18
+ Settings generator for Django-RQ task queue.
19
+
20
+ Converts DjangoRQConfig to Django settings:
21
+ - RQ_QUEUES: Queue configurations
22
+ - RQ_SHOW_ADMIN_LINK: Show link in admin
23
+ - RQ_EXCEPTION_HANDLERS: Exception handlers
24
+ - RQ_API_TOKEN: API authentication token
25
+
26
+ Usage:
27
+ >>> from django_cfg.models.django.django_rq import DjangoRQConfig
28
+ >>> config = DjangoRQConfig(enabled=True)
29
+ >>> generator = DjangoRQSettingsGenerator(config)
30
+ >>> settings = generator.generate()
31
+ >>> 'RQ_QUEUES' in settings
32
+ True
33
+ """
34
+
35
+ def __init__(self, config: DjangoRQConfig, parent_config: Optional["DjangoConfig"] = None):
36
+ """
37
+ Initialize Django-RQ settings generator.
38
+
39
+ Args:
40
+ config: DjangoRQConfig instance
41
+ parent_config: Parent DjangoConfig for accessing global settings like redis_url
42
+ """
43
+ self.config = config
44
+ self.parent_config = parent_config
45
+
46
+ def generate(self) -> Dict[str, Any]:
47
+ """
48
+ Generate Django-RQ settings.
49
+
50
+ Returns:
51
+ Dictionary with RQ_QUEUES and related configuration
52
+ """
53
+ if not self.config.enabled:
54
+ return {}
55
+
56
+ # Use the model's built-in to_django_settings method
57
+ settings = self.config.to_django_settings(parent_config=self.parent_config)
58
+
59
+ return settings
60
+
61
+ def validate(self) -> None:
62
+ """
63
+ Validate Django-RQ configuration.
64
+
65
+ Ensures:
66
+ - At least 'default' queue exists (validated by Pydantic)
67
+ - Queue names are unique (validated by Pydantic)
68
+ - Queue configurations are valid
69
+
70
+ Raises:
71
+ ValueError: If configuration is invalid
72
+ """
73
+ if not self.config.enabled:
74
+ return
75
+
76
+ # Validation is now handled by Pydantic field validators
77
+ # This method is kept for potential custom validation logic
78
+
79
+
80
+ __all__ = ['DjangoRQSettingsGenerator']
@@ -77,9 +77,8 @@ class SettingsOrchestrator:
77
77
  settings.update(self._generate_session_settings())
78
78
  settings.update(self._generate_third_party_settings())
79
79
  settings.update(self._generate_api_settings())
80
- settings.update(self._generate_tasks_settings())
80
+ settings.update(self._generate_django_rq_settings())
81
81
  settings.update(self._generate_grpc_settings())
82
- settings.update(self._generate_django_q2_settings())
83
82
  settings.update(self._generate_tailwind_settings())
84
83
 
85
84
  # Apply additional settings (user overrides)
@@ -216,14 +215,17 @@ class SettingsOrchestrator:
216
215
  except Exception as e:
217
216
  raise ConfigurationError(f"Failed to generate API settings: {e}") from e
218
217
 
219
- def _generate_tasks_settings(self) -> Dict[str, Any]:
220
- """Generate background tasks settings."""
218
+ def _generate_django_rq_settings(self) -> Dict[str, Any]:
219
+ """Generate Django-RQ task queue and scheduler settings."""
220
+ if not hasattr(self.config, "django_rq") or not self.config.django_rq:
221
+ return {}
222
+
221
223
  try:
222
- from .integration_generators.tasks import TasksSettingsGenerator
223
- generator = TasksSettingsGenerator(self.config)
224
+ from .integration_generators.django_rq import DjangoRQSettingsGenerator
225
+ generator = DjangoRQSettingsGenerator(self.config.django_rq, parent_config=self.config)
224
226
  return generator.generate()
225
227
  except Exception as e:
226
- raise ConfigurationError(f"Failed to generate tasks settings: {e}") from e
228
+ raise ConfigurationError(f"Failed to generate Django-RQ settings: {e}") from e
227
229
 
228
230
  def _generate_grpc_settings(self) -> Dict[str, Any]:
229
231
  """Generate gRPC framework settings."""
@@ -234,18 +236,6 @@ class SettingsOrchestrator:
234
236
  except Exception as e:
235
237
  raise ConfigurationError(f"Failed to generate gRPC settings: {e}") from e
236
238
 
237
- def _generate_django_q2_settings(self) -> Dict[str, Any]:
238
- """Generate Django-Q2 task scheduling settings."""
239
- if not hasattr(self.config, "django_q2") or not self.config.django_q2:
240
- return {}
241
-
242
- try:
243
- from .integration_generators.django_q2 import DjangoQ2SettingsGenerator
244
- generator = DjangoQ2SettingsGenerator(self.config.django_q2, parent_config=self.config)
245
- return generator.generate()
246
- except Exception as e:
247
- raise ConfigurationError(f"Failed to generate Django-Q2 settings: {e}") from e
248
-
249
239
  def _generate_tailwind_settings(self) -> Dict[str, Any]:
250
240
  """Generate Tailwind CSS settings."""
251
241
  try:
@@ -416,20 +416,20 @@ class StartupDisplayManager(BaseDisplayManager):
416
416
  task_table.add_column("Value", style="white")
417
417
 
418
418
  # Show real tasks status
419
- tasks_enabled = self.config.should_enable_tasks()
419
+ tasks_enabled = self.config.should_enable_rq()
420
420
  if tasks_enabled:
421
421
  task_table.add_row("Tasks Enabled", "[green]True[/green]")
422
422
  else:
423
423
  task_table.add_row("Tasks Enabled", "[yellow]False[/yellow]")
424
424
 
425
- if hasattr(self.config, 'tasks') and self.config.tasks:
426
- queue_name = getattr(self.config.tasks, 'queue_name', 'default')
427
- task_table.add_row("Queue", f"[yellow]{queue_name}[/yellow]")
425
+ if hasattr(self.config, 'django_rq') and self.config.django_rq:
426
+ queue_names = ', '.join(self.config.django_rq.get_queue_names())
427
+ task_table.add_row("Queues", f"[yellow]{queue_names}[/yellow]")
428
428
  else:
429
- task_table.add_row("Queue", "[yellow]default[/yellow]")
429
+ task_table.add_row("Queues", "[yellow]default[/yellow]")
430
430
 
431
431
  # Add worker command
432
- task_table.add_row("Start Workers", "[bright_blue]poetry run python manage.py runrearq[/bright_blue]")
432
+ task_table.add_row("Start Workers", "[bright_blue]poetry run python manage.py rqworker default[/bright_blue]")
433
433
 
434
434
  task_panel = self.create_full_width_panel(
435
435
  task_table,
@@ -465,13 +465,6 @@ class StartupDisplayManager(BaseDisplayManager):
465
465
  payments_count = 0
466
466
 
467
467
  config = self.config
468
- if config and config.should_enable_tasks():
469
- try:
470
- from django_cfg.modules.django_tasks import extend_constance_config_with_tasks
471
- tasks_fields = extend_constance_config_with_tasks()
472
- tasks_count = len(tasks_fields)
473
- except:
474
- pass
475
468
 
476
469
  if config and config.enable_knowbase:
477
470
  try:
@@ -623,13 +616,6 @@ class StartupDisplayManager(BaseDisplayManager):
623
616
 
624
617
  # Try to get individual app field counts
625
618
  config = self.config
626
- if config and config.should_enable_tasks():
627
- try:
628
- from django_cfg.modules.django_tasks import extend_constance_config_with_tasks
629
- tasks_fields = extend_constance_config_with_tasks()
630
- tasks_count = len(tasks_fields)
631
- except:
632
- pass
633
619
 
634
620
  if config and config.enable_knowbase:
635
621
  try:
@@ -5,8 +5,10 @@ Shared mixins for DRF views and viewsets.
5
5
  """
6
6
  from .admin_api import AdminAPIMixin
7
7
  from .client_api import ClientAPIMixin
8
+ from .superadmin_api import SuperAdminAPIMixin
8
9
 
9
10
  __all__ = [
10
11
  "AdminAPIMixin",
11
12
  "ClientAPIMixin",
13
+ "SuperAdminAPIMixin",
12
14
  ]
@@ -0,0 +1,59 @@
1
+ """
2
+ SuperAdmin API Mixin.
3
+
4
+ Common configuration for superuser-only API endpoints.
5
+ More restrictive than AdminAPIMixin - requires is_superuser flag.
6
+ """
7
+ from rest_framework.authentication import BasicAuthentication, SessionAuthentication
8
+ from rest_framework.permissions import BasePermission
9
+ from rest_framework_simplejwt.authentication import JWTAuthentication
10
+
11
+
12
+ class IsSuperUser(BasePermission):
13
+ """
14
+ Permission that allows access only to superusers.
15
+
16
+ More restrictive than IsAdminUser - requires is_superuser flag.
17
+ Use for sensitive operations like command execution.
18
+ """
19
+
20
+ def has_permission(self, request, view):
21
+ """Check if user is authenticated and is a superuser."""
22
+ return bool(
23
+ request.user and
24
+ request.user.is_authenticated and
25
+ request.user.is_superuser
26
+ )
27
+
28
+
29
+ class SuperAdminAPIMixin:
30
+ """
31
+ Mixin for superuser-only API endpoints.
32
+
33
+ Provides:
34
+ - JWT, Session, and Basic authentication
35
+ - IsSuperUser permission requirement (is_superuser=True)
36
+
37
+ Usage:
38
+ class MyViewSet(SuperAdminAPIMixin, viewsets.ModelViewSet):
39
+ queryset = MyModel.objects.all()
40
+ serializer_class = MySerializer
41
+
42
+ Authentication Methods:
43
+ 1. JWT Token (Bearer): For frontend SPA authentication
44
+ 2. Session: For Django admin integration
45
+ 3. Basic Auth: For testing and scripts
46
+
47
+ All endpoints require superuser privileges.
48
+ Only use this for sensitive operations like:
49
+ - Command execution
50
+ - System configuration changes
51
+ - Direct database operations
52
+ """
53
+
54
+ authentication_classes = [
55
+ JWTAuthentication, # JWT tokens (Bearer)
56
+ SessionAuthentication, # Django session (for admin)
57
+ BasicAuthentication, # HTTP Basic (for testing)
58
+ ]
59
+ permission_classes = [IsSuperUser]
@@ -35,7 +35,7 @@ from .base.module import BaseCfgAutoModule
35
35
  from .django.axes import AxesConfig
36
36
  from .django.constance import ConstanceConfig, ConstanceField
37
37
  from .django.crypto_fields import CryptoFieldsConfig
38
- from .django.django_q2 import DjangoQ2Config, DjangoQ2ScheduleConfig
38
+ from .django.django_rq import DjangoRQConfig, RQQueueConfig
39
39
  from .django.environment import EnvironmentConfig
40
40
  from .django.openapi import OpenAPIClientConfig
41
41
  from .infrastructure.cache import CacheConfig
@@ -80,8 +80,8 @@ __all__ = [
80
80
  "EnvironmentConfig",
81
81
  "ConstanceConfig",
82
82
  "ConstanceField",
83
- "DjangoQ2Config",
84
- "DjangoQ2ScheduleConfig",
83
+ "DjangoRQConfig",
84
+ "RQQueueConfig",
85
85
  "OpenAPIClientConfig",
86
86
  "UnfoldConfig",
87
87
  "AxesConfig",
@@ -6,7 +6,7 @@ Django integrations and extensions.
6
6
 
7
7
  from .axes import AxesConfig
8
8
  from .constance import ConstanceConfig, ConstanceField
9
- from .django_q2 import DjangoQ2Config, DjangoQ2ScheduleConfig
9
+ from .django_rq import DjangoRQConfig, RQQueueConfig
10
10
  from .environment import EnvironmentConfig
11
11
  from .openapi import OpenAPIClientConfig
12
12
 
@@ -14,8 +14,8 @@ __all__ = [
14
14
  "EnvironmentConfig",
15
15
  "ConstanceConfig",
16
16
  "ConstanceField",
17
- "DjangoQ2Config",
18
- "DjangoQ2ScheduleConfig",
17
+ "DjangoRQConfig",
18
+ "RQQueueConfig",
19
19
  "OpenAPIClientConfig",
20
20
  "AxesConfig",
21
21
  ]