django-cfg 1.5.1__py3-none-any.whl → 1.5.3__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.3.dist-info}/METADATA +5 -6
  74. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.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.3.dist-info}/WHEEL +0 -0
  120. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/entry_points.txt +0 -0
  121. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,514 +0,0 @@
1
- """
2
- Django-Q2 Configuration for django-cfg.
3
-
4
- Type-safe configuration for django-q2 (modern fork of django-q) with automatic
5
- Django settings generation and support for scheduled tasks.
6
-
7
- Django-Q2 is the actively maintained fork: https://github.com/django-q2/django-q2
8
-
9
- Features:
10
- - Type-safe scheduled task definitions
11
- - Django management command support
12
- - Cron-style and interval-based scheduling
13
- - Redis/database broker configuration
14
- - Queue management
15
- - Task result storage
16
- - Monitoring and logging
17
- - Built-in admin interface
18
- - Python 3.8+ and Django 3.2+ support
19
-
20
- Migration from django-crontab to django-q2:
21
- 1. Replace django-crontab with django-q2 in requirements
22
- 2. Convert CrontabConfig to DjangoQ2Config
23
- 3. Cron expressions stay the same
24
- 4. Add additional features like intervals, hooks, retries
25
- 5. Built-in admin interface for monitoring
26
- 6. No need to run 'crontab add' - uses Django's own scheduler
27
-
28
- Example:
29
- ```python
30
- # Old django-crontab
31
- crontab_config = CrontabConfig(
32
- jobs=[
33
- CrontabJobConfig(
34
- name="Sync balances",
35
- minute="0",
36
- hour="*/1", # Every hour
37
- command="sync_account_balances",
38
- )
39
- ]
40
- )
41
-
42
- # New django-q2
43
- django_q2_config = DjangoQ2Config(
44
- schedules=[
45
- DjangoQ2ScheduleConfig(
46
- name="Sync balances",
47
- schedule_type="hourly", # Simpler!
48
- command="sync_account_balances",
49
- )
50
- ]
51
- )
52
- ```
53
- """
54
-
55
- from typing import Any, Dict, List, Literal, Optional, Union
56
-
57
- from pydantic import BaseModel, ConfigDict, Field, field_validator
58
-
59
-
60
- class DjangoQ2ScheduleConfig(BaseModel):
61
- """
62
- Configuration for a single Django-Q2 scheduled task.
63
-
64
- Supports both cron-style and interval-based scheduling.
65
-
66
- Schedule types:
67
- - cron: Traditional cron expression (e.g., "0 0 * * *")
68
- - minutes: Every N minutes (e.g., minutes=15)
69
- - hourly: Every hour (at minute 0)
70
- - daily: Every day (at midnight)
71
- - weekly: Every week (Sunday at midnight)
72
- - monthly: Every month (1st at midnight)
73
- - yearly: Every year (Jan 1st at midnight)
74
- - once: Run once at next scheduled time
75
- """
76
-
77
- model_config = ConfigDict(
78
- validate_assignment=True,
79
- extra="forbid",
80
- )
81
-
82
- # Task identification
83
- name: str = Field(
84
- ...,
85
- description="Human-readable task name (unique identifier)",
86
- min_length=1,
87
- max_length=100,
88
- )
89
-
90
- # Schedule type
91
- schedule_type: Literal["cron", "minutes", "hourly", "daily", "weekly", "monthly", "yearly", "once"] = Field(
92
- default="cron",
93
- description="Schedule type: cron, minutes, hourly, daily, weekly, monthly, yearly, once",
94
- )
95
-
96
- # Cron-style schedule (when schedule_type="cron")
97
- cron: Optional[str] = Field(
98
- default=None,
99
- description="Cron expression (e.g., '0 0 * * *' for daily at midnight)",
100
- pattern=r"^[\d\*\-\,\/\s]+$",
101
- )
102
-
103
- # Interval-based schedule (when schedule_type="minutes")
104
- minutes: Optional[int] = Field(
105
- default=None,
106
- ge=1,
107
- le=525600, # Max 1 year in minutes
108
- description="Run every N minutes (when schedule_type='minutes')",
109
- )
110
-
111
- # Task execution configuration
112
- func: Optional[str] = Field(
113
- default=None,
114
- description="Function path (e.g., 'django.core.management.call_command' or 'myapp.tasks.my_task'). Auto-set when 'command' is provided.",
115
- )
116
-
117
- # Function arguments
118
- args: Optional[List[Any]] = Field(
119
- default=None,
120
- description="Positional arguments for the function",
121
- )
122
-
123
- kwargs: Optional[Dict[str, Any]] = Field(
124
- default=None,
125
- description="Keyword arguments for the function",
126
- )
127
-
128
- # Management command support (shortcut)
129
- command: Optional[str] = Field(
130
- default=None,
131
- description="Django management command name (auto-sets func to call_command)",
132
- )
133
-
134
- command_args: Optional[List[str]] = Field(
135
- default=None,
136
- description="Management command arguments",
137
- )
138
-
139
- command_kwargs: Optional[Dict[str, Any]] = Field(
140
- default=None,
141
- description="Management command keyword arguments",
142
- )
143
-
144
- # Task options
145
- enabled: bool = Field(
146
- default=True,
147
- description="Whether task is enabled",
148
- )
149
-
150
- queue: Optional[str] = Field(
151
- default=None,
152
- description="Queue name (None = default queue)",
153
- )
154
-
155
- timeout: Optional[int] = Field(
156
- default=None,
157
- ge=1,
158
- le=86400, # Max 24 hours
159
- description="Task timeout in seconds",
160
- )
161
-
162
- repeats: int = Field(
163
- default=-1,
164
- description="Number of times to repeat (-1 = infinite)",
165
- )
166
-
167
- hook: Optional[str] = Field(
168
- default=None,
169
- description="Hook function to call after task completion",
170
- )
171
-
172
- cluster: Optional[str] = Field(
173
- default=None,
174
- description="Cluster name (for multi-cluster setups)",
175
- )
176
-
177
- def model_post_init(self, __context: Any) -> None:
178
- """Validate and setup configuration after initialization."""
179
- # Validate that either func or command is provided
180
- if not self.func and not self.command:
181
- raise ValueError("Either 'func' or 'command' must be provided")
182
-
183
- # Setup management command
184
- if self.command:
185
- self.func = "django.core.management.call_command"
186
- args = [self.command]
187
- if self.command_args:
188
- args.extend(self.command_args)
189
- self.args = args
190
- if self.command_kwargs:
191
- self.kwargs = self.command_kwargs
192
-
193
- # Validate schedule configuration
194
- if self.schedule_type == "cron" and not self.cron:
195
- raise ValueError("'cron' must be set when schedule_type is 'cron'")
196
-
197
- if self.schedule_type == "minutes" and not self.minutes:
198
- raise ValueError("'minutes' must be set when schedule_type is 'minutes'")
199
-
200
- def to_django_q_format(self) -> Dict[str, Any]:
201
- """
202
- Convert to Django-Q Schedule format.
203
-
204
- Returns:
205
- Dictionary for ORM.create() or schedule creation
206
- """
207
- from django.utils import timezone
208
- from datetime import timedelta
209
-
210
- # Map our schedule types to Django-Q2 constants
211
- type_mapping = {
212
- "once": "O",
213
- "minutes": "I",
214
- "hourly": "H",
215
- "daily": "D",
216
- "weekly": "W",
217
- "monthly": "M",
218
- "yearly": "Y",
219
- "cron": "C",
220
- }
221
-
222
- config = {
223
- "name": self.name,
224
- "func": self.func,
225
- "schedule_type": type_mapping.get(self.schedule_type, self.schedule_type.upper()),
226
- # Set next_run to NOW + 10 seconds for immediate execution on qcluster start
227
- "next_run": timezone.now() + timedelta(seconds=10),
228
- }
229
-
230
- # Convert args list to tuple for Django-Q2 scheduler compatibility
231
- # Django-Q2 scheduler.py:66-69 expects tuple format, not list
232
- # If list is provided, scheduler wraps it: (list,) instead of converting list -> tuple
233
- if self.args:
234
- config["args"] = tuple(self.args) if isinstance(self.args, list) else self.args
235
-
236
- if self.kwargs:
237
- config["kwargs"] = self.kwargs
238
-
239
- if self.schedule_type == "cron" and self.cron:
240
- config["cron"] = self.cron
241
-
242
- if self.schedule_type == "minutes" and self.minutes:
243
- config["minutes"] = self.minutes
244
-
245
- if self.queue:
246
- config["queue"] = self.queue
247
-
248
- if self.timeout:
249
- config["timeout"] = self.timeout
250
-
251
- if self.repeats != -1:
252
- config["repeats"] = self.repeats
253
-
254
- if self.hook:
255
- config["hook"] = self.hook
256
-
257
- if self.cluster:
258
- config["cluster"] = self.cluster
259
-
260
- return config
261
-
262
-
263
- class DjangoQ2Config(BaseModel):
264
- """
265
- Complete Django-Q2 configuration container.
266
-
267
- Integrates with django-q2 (modern fork) for scheduled and async task execution.
268
- Automatically adds django_q to INSTALLED_APPS when enabled.
269
-
270
- Installation:
271
- pip install django-q2[redis]
272
-
273
- Example:
274
- ```python
275
- # MAGIC: broker_url automatically uses config.redis_url! 🎉
276
- # Just set redis_url once in your DjangoConfig:
277
- # redis_url: Optional[str] = env.redis_url
278
-
279
- django_q2_config = DjangoQ2Config(
280
- enabled=True,
281
- # broker_url is auto-detected from config.redis_url!
282
- schedules=[
283
- DjangoQ2ScheduleConfig(
284
- name="Sync balances every hour",
285
- schedule_type="hourly",
286
- command="sync_account_balances",
287
- ),
288
- DjangoQ2ScheduleConfig(
289
- name="Cleanup old data daily",
290
- schedule_type="cron",
291
- cron="0 2 * * *", # 2 AM daily
292
- command="cleanup_old_data",
293
- command_kwargs={"days": 30},
294
- ),
295
- DjangoQ2ScheduleConfig(
296
- name="Quick check every 5 minutes",
297
- schedule_type="minutes",
298
- minutes=5,
299
- command="health_check",
300
- ),
301
- ],
302
- )
303
- ```
304
-
305
- Schedule Synchronization (AUTOMATIC):
306
- When Django-Q2 is enabled, schedules are automatically synced after migrations.
307
-
308
- The module 'django_cfg.modules.django_q2' is automatically added to INSTALLED_APPS
309
- when django_q2.enabled=True, so you don't need to add it manually.
310
-
311
- It uses Django's post_migrate signal to sync schedules from config to database.
312
-
313
- Manual sync (optional):
314
- ```bash
315
- python manage.py sync_django_q_schedules
316
- ```
317
-
318
- Admin interface:
319
- - Visit /admin/django_q/ to view tasks and schedules
320
- - Monitor task execution, failures, and performance
321
- - Manually trigger scheduled tasks
322
- - View task results and logs
323
- """
324
-
325
- model_config = ConfigDict(
326
- validate_assignment=True,
327
- extra="forbid",
328
- )
329
-
330
- enabled: bool = Field(
331
- default=True,
332
- description="Enable Django-Q (auto-adds django_q to INSTALLED_APPS)",
333
- )
334
-
335
- schedules: List[DjangoQ2ScheduleConfig] = Field(
336
- default_factory=list,
337
- description="List of scheduled tasks",
338
- )
339
-
340
- # Django-Q broker configuration
341
- broker_url: str = Field(
342
- default="redis://localhost:6379/0",
343
- description="Broker URL (Redis recommended for production)",
344
- )
345
-
346
- broker_class: Literal["redis", "orm"] = Field(
347
- default="redis",
348
- description="Broker backend class (redis or orm)",
349
- )
350
-
351
- # Queue configuration
352
- queue_limit: Optional[int] = Field(
353
- default=50,
354
- ge=1,
355
- description="Maximum tasks in queue before rejecting new ones",
356
- )
357
-
358
- workers: int = Field(
359
- default=4,
360
- ge=1,
361
- le=32,
362
- description="Number of worker processes",
363
- )
364
-
365
- timeout: int = Field(
366
- default=300,
367
- ge=1,
368
- le=86400, # Max 24 hours
369
- description="Default task timeout in seconds",
370
- )
371
-
372
- retry: int = Field(
373
- default=3600,
374
- ge=0,
375
- description="Seconds to wait before retrying failed tasks (0 = no retry)",
376
- )
377
-
378
- # Task result configuration
379
- save_limit: int = Field(
380
- default=250,
381
- ge=0,
382
- description="Maximum number of successful tasks to save (0 = unlimited)",
383
- )
384
-
385
- cached: int = Field(
386
- default=500,
387
- ge=0,
388
- description="Maximum number of tasks to cache (0 = disabled)",
389
- )
390
-
391
- # Monitoring
392
- monitor_interval: int = Field(
393
- default=30,
394
- ge=1,
395
- description="Seconds between monitor checks",
396
- )
397
-
398
- # Logging
399
- log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field(
400
- default="INFO",
401
- description="Django-Q log level",
402
- )
403
-
404
- # Advanced options
405
- compress: bool = Field(
406
- default=False,
407
- description="Compress task data",
408
- )
409
-
410
- catch_up: bool = Field(
411
- default=True,
412
- description="Run missed scheduled tasks immediately",
413
- )
414
-
415
- sync: bool = Field(
416
- default=False,
417
- description="Run tasks synchronously (for testing)",
418
- )
419
-
420
- def get_enabled_schedules(self) -> List[DjangoQ2ScheduleConfig]:
421
- """Get list of enabled schedules."""
422
- return [schedule for schedule in self.schedules if schedule.enabled]
423
-
424
- def to_django_settings(self, parent_config: Optional[Any] = None) -> Dict[str, Any]:
425
- """
426
- Convert to Django settings dictionary.
427
-
428
- Generates Q_CLUSTER configuration for Django-Q2.
429
-
430
- Args:
431
- parent_config: Optional parent DjangoConfig for accessing redis_url
432
-
433
- Note: Schedules are created via Django ORM, not settings.
434
- Use management command: python manage.py qcluster
435
- """
436
- if not self.enabled:
437
- return {}
438
-
439
- # Auto-detect redis_url from parent config if not explicitly set
440
- broker_url = self.broker_url
441
- if broker_url == "redis://localhost:6379/0" and parent_config:
442
- # Use redis_url from parent config if available
443
- if hasattr(parent_config, 'redis_url') and parent_config.redis_url:
444
- broker_url = parent_config.redis_url
445
-
446
- # Map short broker names to full class paths
447
- broker_class_map = {
448
- "redis": "django_q.brokers.redis_broker.Redis",
449
- "orm": "django_q.brokers.orm.ORM",
450
- }
451
-
452
- cluster_config = {
453
- # Broker
454
- "name": "django_cfg_cluster",
455
- "broker": broker_url,
456
- "broker_class": broker_class_map.get(self.broker_class, self.broker_class),
457
-
458
- # Queue
459
- "queue_limit": self.queue_limit,
460
- "workers": self.workers,
461
- "timeout": self.timeout,
462
- "retry": self.retry,
463
-
464
- # Results
465
- "save_limit": self.save_limit,
466
- "cached": self.cached,
467
-
468
- # Monitoring
469
- "monitor": self.monitor_interval,
470
-
471
- # Logging
472
- "log_level": self.log_level,
473
-
474
- # Advanced
475
- "compress": self.compress,
476
- "catch_up": self.catch_up,
477
- "sync": self.sync,
478
-
479
- # Django integration
480
- "orm": "default",
481
- }
482
-
483
- # CRITICAL FIX: Django-Q2 uses 'redis' parameter, NOT 'broker'!
484
- # The 'broker' parameter is ignored by django-q2.
485
- # We must set 'redis' parameter to the broker_url string.
486
- if self.broker_class == "redis":
487
- # Set redis parameter to broker_url (Django-Q2 accepts redis:// URL string)
488
- cluster_config["redis"] = broker_url
489
-
490
- settings = {
491
- "Q_CLUSTER": cluster_config
492
- }
493
-
494
- return settings
495
-
496
- def get_schedule_by_name(self, name: str) -> Optional[DjangoQ2ScheduleConfig]:
497
- """Get schedule by name."""
498
- for schedule in self.schedules:
499
- if schedule.name == name:
500
- return schedule
501
- return None
502
-
503
- def get_schedules_by_command(self, command: str) -> List[DjangoQ2ScheduleConfig]:
504
- """Get all schedules for a specific command."""
505
- return [
506
- schedule for schedule in self.schedules
507
- if schedule.command == command
508
- ]
509
-
510
-
511
- __all__ = [
512
- "DjangoQ2ScheduleConfig",
513
- "DjangoQ2Config",
514
- ]
@@ -1,49 +0,0 @@
1
- """
2
- Task processing configuration models for Django-CFG.
3
-
4
- This module provides type-safe Pydantic models for configuring background task
5
- processing with ReArq, including worker management, queue configuration,
6
- and monitoring settings.
7
-
8
- Architecture:
9
- config.py - Main TaskConfig and enums
10
- backends.py - RearqConfig
11
- utils.py - Utility functions
12
-
13
- Example:
14
- ```python
15
- from django_cfg.models.tasks import TaskConfig, RearqConfig
16
-
17
- # Basic configuration
18
- tasks = TaskConfig(
19
- enabled=True,
20
- rearq=RearqConfig(
21
- redis_url="redis://localhost:6379/0",
22
- max_jobs=10,
23
- )
24
- )
25
-
26
- # Get environment-aware defaults
27
- from django_cfg.models.tasks import get_default_task_config
28
- tasks = get_default_task_config(debug=True)
29
- ```
30
- """
31
-
32
- from .backends import RearqConfig
33
- from .config import QueuePriority, TaskBackend, TaskConfig
34
- from .utils import get_default_task_config, get_smart_queues, validate_task_config
35
-
36
- __all__ = [
37
- # Main configuration
38
- "TaskConfig",
39
- "TaskBackend",
40
- "QueuePriority",
41
-
42
- # Backend configurations
43
- "RearqConfig",
44
-
45
- # Utility functions
46
- "get_default_task_config",
47
- "validate_task_config",
48
- "get_smart_queues",
49
- ]
@@ -1,122 +0,0 @@
1
- """
2
- Backend-specific configurations.
3
-
4
- Contains ReArq configuration model.
5
- Size: ~100 lines (focused on backend settings)
6
- """
7
-
8
- import logging
9
- from typing import Any, Dict
10
-
11
- from pydantic import BaseModel, Field, field_validator
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- class RearqConfig(BaseModel):
17
- """
18
- ReArq-specific configuration with production-ready defaults.
19
-
20
- This model provides comprehensive configuration for ReArq async background
21
- task processing, including Redis settings, worker configuration,
22
- and job retry policies.
23
-
24
- Example:
25
- ```python
26
- from django_cfg.models.tasks import RearqConfig
27
-
28
- rearq = RearqConfig(
29
- redis_url="redis://localhost:6379/0",
30
- db_url="sqlite://./rearq.db",
31
- max_jobs=10,
32
- job_timeout=300,
33
- )
34
- ```
35
- """
36
-
37
- # === Core Settings ===
38
- redis_url: str = Field(
39
- default="redis://localhost:6379/0",
40
- description="Redis connection URL for task queue"
41
- )
42
- db_url: str = Field(
43
- default="sqlite://./rearq.db",
44
- description="Database URL for job persistence (Tortoise ORM)"
45
- )
46
-
47
- # === Worker Settings ===
48
- max_jobs: int = Field(
49
- default=10,
50
- ge=1,
51
- le=100,
52
- description="Maximum concurrent jobs per worker"
53
- )
54
- job_timeout: int = Field(
55
- default=300,
56
- ge=30,
57
- le=3600,
58
- description="Default job timeout in seconds"
59
- )
60
- job_retry: int = Field(
61
- default=3,
62
- ge=0,
63
- le=10,
64
- description="Default number of retries for failed jobs"
65
- )
66
- job_retry_after: int = Field(
67
- default=60,
68
- ge=1,
69
- description="Delay in seconds before retrying failed job"
70
- )
71
-
72
- # === Cleanup Settings ===
73
- keep_job_days: int | None = Field(
74
- default=7,
75
- ge=1,
76
- description="Days to keep job history (None = forever)"
77
- )
78
-
79
- @field_validator("redis_url")
80
- @classmethod
81
- def validate_redis_url(cls, v: str) -> str:
82
- """Validate Redis URL format."""
83
- if not v.startswith(("redis://", "rediss://")):
84
- raise ValueError("Redis URL must start with redis:// or rediss://")
85
- return v
86
-
87
- @field_validator("db_url")
88
- @classmethod
89
- def validate_db_url(cls, v: str) -> str:
90
- """Validate database URL format."""
91
- valid_schemes = ("sqlite://", "postgres://", "postgresql://", "mysql://")
92
- if not v.startswith(valid_schemes):
93
- raise ValueError(f"Database URL must start with one of: {valid_schemes}")
94
- return v
95
-
96
- def to_django_settings(self) -> Dict[str, Any]:
97
- """
98
- Generate Django settings dictionary.
99
-
100
- Returns:
101
- Dictionary with ReArq configuration for Django settings
102
-
103
- Example:
104
- >>> config = RearqConfig()
105
- >>> settings = config.to_django_settings()
106
- >>> "REARQ_REDIS_URL" in settings
107
- True
108
- """
109
- return {
110
- "REARQ_REDIS_URL": self.redis_url,
111
- "REARQ_DB_URL": self.db_url,
112
- "REARQ_MAX_JOBS": self.max_jobs,
113
- "REARQ_JOB_TIMEOUT": self.job_timeout,
114
- "REARQ_JOB_RETRY": self.job_retry,
115
- "REARQ_JOB_RETRY_AFTER": self.job_retry_after,
116
- "REARQ_KEEP_JOB_DAYS": self.keep_job_days,
117
- }
118
-
119
-
120
- __all__ = [
121
- "RearqConfig",
122
- ]