django-cfg 1.1.61__py3-none-any.whl → 1.1.63__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 (28) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/management/commands/rundramatiq.py +174 -202
  3. django_cfg/modules/django_tasks.py +54 -428
  4. django_cfg/modules/dramatiq_setup.py +16 -0
  5. {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/METADATA +145 -4
  6. {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/RECORD +9 -27
  7. django_cfg/apps/accounts/tests/__init__.py +0 -1
  8. django_cfg/apps/accounts/tests/test_models.py +0 -412
  9. django_cfg/apps/accounts/tests/test_otp_views.py +0 -143
  10. django_cfg/apps/accounts/tests/test_serializers.py +0 -331
  11. django_cfg/apps/accounts/tests/test_services.py +0 -401
  12. django_cfg/apps/accounts/tests/test_signals.py +0 -110
  13. django_cfg/apps/accounts/tests/test_views.py +0 -255
  14. django_cfg/apps/newsletter/tests/__init__.py +0 -1
  15. django_cfg/apps/newsletter/tests/run_tests.py +0 -47
  16. django_cfg/apps/newsletter/tests/test_email_integration.py +0 -256
  17. django_cfg/apps/newsletter/tests/test_email_tracking.py +0 -332
  18. django_cfg/apps/newsletter/tests/test_newsletter_manager.py +0 -83
  19. django_cfg/apps/newsletter/tests/test_newsletter_models.py +0 -157
  20. django_cfg/apps/support/tests/__init__.py +0 -0
  21. django_cfg/apps/support/tests/test_models.py +0 -106
  22. django_cfg/apps/tasks/@docs/CONFIGURATION.md +0 -663
  23. django_cfg/apps/tasks/@docs/README.md +0 -195
  24. django_cfg/apps/tasks/@docs/TASKS_QUEUES.md +0 -423
  25. django_cfg/apps/tasks/@docs/TROUBLESHOOTING.md +0 -506
  26. {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/WHEEL +0 -0
  27. {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/entry_points.txt +0 -0
  28. {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/licenses/LICENSE +0 -0
@@ -1,663 +0,0 @@
1
- # ⚙️ Django-CFG Tasks Configuration Guide
2
-
3
- ## 🎯 Overview
4
-
5
- Complete configuration reference for Django-CFG task system. Covers all settings, environment variables, and deployment configurations.
6
-
7
- **TAGS**: `configuration, settings, deployment, dramatiq, redis`
8
-
9
- ---
10
-
11
- ## 🏗️ Configuration Models %%PRIORITY:HIGH%%
12
-
13
- ### TaskConfig (Main Configuration)
14
-
15
- ```python
16
- from django_cfg.models.tasks import TaskConfig, DramatiqConfig
17
-
18
- config = TaskConfig(
19
- enabled=True, # Enable task system
20
- backend="dramatiq", # Task backend (only dramatiq supported)
21
- dramatiq=DramatiqConfig(...), # Dramatiq-specific settings
22
- auto_discover_tasks=True, # Auto-discover task modules
23
- task_modules=["tasks"] # Module names to search for tasks
24
- )
25
- ```
26
-
27
- **Field Details**:
28
- - `enabled`: Master switch for entire task system
29
- - `backend`: Currently only `TaskBackend.DRAMATIQ` supported
30
- - `dramatiq`: Nested Dramatiq configuration (see below)
31
- - `auto_discover_tasks`: Automatically find task modules in Django apps
32
- - `task_modules`: List of module names to search (e.g., "tasks", "background")
33
-
34
- ---
35
-
36
- ### DramatiqConfig (Dramatiq Settings)
37
-
38
- ```python
39
- from django_cfg.models.tasks import DramatiqConfig
40
-
41
- dramatiq_config = DramatiqConfig(
42
- # === Redis Configuration ===
43
- redis_db=2, # Redis database number %%PRIORITY:HIGH%%
44
- redis_key_prefix="dramatiq", # Key prefix for Redis keys
45
- redis_socket_timeout=5, # Connection timeout (seconds)
46
- redis_socket_connect_timeout=5, # Connect timeout (seconds)
47
- redis_socket_keepalive=True, # Enable TCP keepalive
48
- redis_socket_keepalive_options={},
49
- redis_health_check_interval=30, # Health check interval (seconds)
50
-
51
- # === Worker Configuration ===
52
- processes=2, # Number of worker processes
53
- threads=4, # Threads per process
54
-
55
- # === Queue Configuration ===
56
- queues=[ # Available queues
57
- "default", "high", "low",
58
- "vehicles", "tax", "parsing", "knowledge"
59
- ],
60
-
61
- # === Middleware Stack ===
62
- middleware=[ # Middleware in order %%PRIORITY:HIGH%%
63
- "dramatiq.middleware.AgeLimit",
64
- "dramatiq.middleware.TimeLimit",
65
- "dramatiq.middleware.Callbacks",
66
- "dramatiq.middleware.Retries",
67
- "dramatiq.middleware.Shutdown",
68
- ],
69
-
70
- # === Timeouts & Limits ===
71
- max_age=3600000, # Max message age (ms)
72
- max_retries=3, # Default retry count
73
- min_backoff=15000, # Min retry backoff (ms)
74
- max_backoff=300000, # Max retry backoff (ms)
75
-
76
- # === Worker Settings ===
77
- worker=WorkerConfig(
78
- threads=4, # Threads per worker
79
- shutdown_timeout=30, # Graceful shutdown timeout
80
- log_level="INFO" # Worker log level
81
- ),
82
-
83
- # === Monitoring ===
84
- prometheus_enabled=False, # Disable Prometheus by default
85
- prometheus_http_host="127.0.0.1",
86
- prometheus_http_port=9191,
87
-
88
- # === Admin Interface ===
89
- admin_enabled=True # Enable Django admin integration
90
- )
91
- ```
92
-
93
- ---
94
-
95
- ## 🔧 Environment-Specific Configurations
96
-
97
- ### Development Configuration
98
-
99
- ```python
100
- # config/dev.yaml
101
- tasks:
102
- enabled: true
103
- dramatiq:
104
- redis_db: 2
105
- processes: 1 # Single process for development
106
- threads: 2 # Fewer threads
107
- queues:
108
- - default
109
- - knowledge # Only essential queues
110
- prometheus_enabled: false
111
- ```
112
-
113
- ### Production Configuration
114
-
115
- ```python
116
- # config/prod.yaml
117
- tasks:
118
- enabled: true
119
- dramatiq:
120
- redis_db: 2
121
- processes: 4 # More processes for production
122
- threads: 8 # More threads
123
- queues: # All queues
124
- - default
125
- - high
126
- - low
127
- - vehicles
128
- - tax
129
- - parsing
130
- - knowledge
131
- prometheus_enabled: true
132
- prometheus_http_port: 9191
133
- max_retries: 5 # More retries in production
134
- ```
135
-
136
- ### Testing Configuration
137
-
138
- ```python
139
- # config/test.yaml
140
- tasks:
141
- enabled: false # Disable in tests
142
- # OR for integration tests:
143
- enabled: true
144
- dramatiq:
145
- redis_db: 15 # Use different DB for tests
146
- processes: 1
147
- threads: 1
148
- ```
149
-
150
- ---
151
-
152
- ## 🐳 Docker Configuration
153
-
154
- ### Docker Compose
155
-
156
- ```yaml
157
- version: '3.8'
158
- services:
159
- redis:
160
- image: redis:7-alpine
161
- ports:
162
- - "6379:6379"
163
- volumes:
164
- - redis_data:/data
165
- command: redis-server --appendonly yes
166
-
167
- django:
168
- build: .
169
- environment:
170
- - DJANGO_SETTINGS_MODULE=api.settings
171
- - REDIS_URL=redis://redis:6379/0
172
- depends_on:
173
- - redis
174
-
175
- dramatiq-workers:
176
- build: .
177
- command: python manage.py rundramatiq --processes 2 --threads 4
178
- environment:
179
- - DJANGO_SETTINGS_MODULE=api.settings
180
- - REDIS_URL=redis://redis:6379/0
181
- depends_on:
182
- - redis
183
- - django
184
- restart: unless-stopped
185
-
186
- volumes:
187
- redis_data:
188
- ```
189
-
190
- ### Dockerfile for Workers
191
-
192
- ```dockerfile
193
- FROM python:3.11-slim
194
-
195
- WORKDIR /app
196
- COPY requirements.txt .
197
- RUN pip install -r requirements.txt
198
-
199
- COPY . .
200
-
201
- # Health check for workers
202
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
203
- CMD python -c "
204
- import redis
205
- r = redis.Redis(host='redis', port=6379, db=2)
206
- r.ping()
207
- " || exit 1
208
-
209
- CMD ["python", "manage.py", "rundramatiq", "--processes", "2", "--threads", "4"]
210
- ```
211
-
212
- ---
213
-
214
- ## 🔄 Process Management
215
-
216
- ### Systemd Service
217
-
218
- ```ini
219
- # /etc/systemd/system/dramatiq-workers.service
220
- [Unit]
221
- Description=Dramatiq Background Workers
222
- After=network.target redis.service postgresql.service
223
- Requires=redis.service
224
-
225
- [Service]
226
- Type=simple
227
- User=www-data
228
- Group=www-data
229
- WorkingDirectory=/opt/myproject
230
- Environment=DJANGO_SETTINGS_MODULE=api.settings
231
- Environment=PYTHONPATH=/opt/myproject
232
- ExecStart=/opt/myproject/venv/bin/python manage.py rundramatiq --processes 4 --threads 8
233
- ExecReload=/bin/kill -HUP $MAINPID
234
- KillMode=mixed
235
- TimeoutStopSec=30
236
- Restart=always
237
- RestartSec=10
238
-
239
- # Resource limits
240
- LimitNOFILE=65536
241
- MemoryMax=2G
242
- CPUQuota=200%
243
-
244
- # Logging
245
- StandardOutput=journal
246
- StandardError=journal
247
- SyslogIdentifier=dramatiq-workers
248
-
249
- [Install]
250
- WantedBy=multi-user.target
251
- ```
252
-
253
- **Management Commands**:
254
- ```bash
255
- # Enable and start
256
- sudo systemctl enable dramatiq-workers
257
- sudo systemctl start dramatiq-workers
258
-
259
- # Status and logs
260
- sudo systemctl status dramatiq-workers
261
- sudo journalctl -u dramatiq-workers -f
262
-
263
- # Restart workers
264
- sudo systemctl restart dramatiq-workers
265
- ```
266
-
267
- ### Supervisor Configuration
268
-
269
- ```ini
270
- # /etc/supervisor/conf.d/dramatiq-workers.conf
271
- [program:dramatiq-workers]
272
- command=/opt/myproject/venv/bin/python manage.py rundramatiq --processes 4 --threads 8
273
- directory=/opt/myproject
274
- user=www-data
275
- autostart=true
276
- autorestart=true
277
- startsecs=10
278
- startretries=3
279
- redirect_stderr=true
280
- stdout_logfile=/var/log/dramatiq/workers.log
281
- stdout_logfile_maxbytes=50MB
282
- stdout_logfile_backups=10
283
- environment=DJANGO_SETTINGS_MODULE="api.settings",PYTHONPATH="/opt/myproject"
284
-
285
- # Resource limits
286
- process_name=%(program_name)s_%(process_num)02d
287
- numprocs=1
288
- ```
289
-
290
- **Management Commands**:
291
- ```bash
292
- # Reload configuration
293
- sudo supervisorctl reread
294
- sudo supervisorctl update
295
-
296
- # Control workers
297
- sudo supervisorctl start dramatiq-workers
298
- sudo supervisorctl stop dramatiq-workers
299
- sudo supervisorctl restart dramatiq-workers
300
- sudo supervisorctl status dramatiq-workers
301
- ```
302
-
303
- ---
304
-
305
- ## 🔐 Security Configuration
306
-
307
- ### Redis Security
308
-
309
- ```bash
310
- # /etc/redis/redis.conf
311
- bind 127.0.0.1 # Bind to localhost only
312
- requirepass your_strong_password # Set password
313
- protected-mode yes # Enable protected mode
314
- port 6379 # Default port (consider changing)
315
-
316
- # Disable dangerous commands
317
- rename-command FLUSHDB ""
318
- rename-command FLUSHALL ""
319
- rename-command KEYS ""
320
- rename-command CONFIG ""
321
- ```
322
-
323
- ### Django Settings
324
-
325
- ```python
326
- # settings/production.py
327
- DRAMATIQ_BROKER = {
328
- "BROKER": "dramatiq.brokers.redis.RedisBroker",
329
- "OPTIONS": {
330
- "url": "redis://:password@localhost:6379/2", # With password
331
- "connection_pool_kwargs": {
332
- "max_connections": 20,
333
- "retry_on_timeout": True,
334
- },
335
- },
336
- "MIDDLEWARE": [
337
- "dramatiq.middleware.AgeLimit",
338
- "dramatiq.middleware.TimeLimit",
339
- "dramatiq.middleware.Callbacks",
340
- "dramatiq.middleware.Retries",
341
- "dramatiq.middleware.Shutdown",
342
- ],
343
- }
344
-
345
- # Security headers for Prometheus endpoint
346
- if DRAMATIQ_CONFIG.get('prometheus_enabled'):
347
- ALLOWED_HOSTS.append('monitoring.internal')
348
- ```
349
-
350
- ---
351
-
352
- ## 📊 Monitoring Configuration
353
-
354
- ### Prometheus Integration
355
-
356
- ```python
357
- # Enable Prometheus in configuration
358
- dramatiq_config = DramatiqConfig(
359
- prometheus_enabled=True,
360
- prometheus_http_host="0.0.0.0", # Allow external access
361
- prometheus_http_port=9191,
362
- )
363
- ```
364
-
365
- **Prometheus Scrape Config**:
366
- ```yaml
367
- # prometheus.yml
368
- scrape_configs:
369
- - job_name: 'dramatiq-workers'
370
- static_configs:
371
- - targets: ['worker1:9191', 'worker2:9191']
372
- scrape_interval: 15s
373
- metrics_path: /metrics
374
- ```
375
-
376
- ### Health Check Endpoint
377
-
378
- ```python
379
- # Add to Django URLs
380
- from django_cfg.modules.django_tasks import get_task_health
381
-
382
- def health_check(request):
383
- health = get_task_health()
384
- status = 200 if health['enabled'] and health['redis_connection'] else 503
385
- return JsonResponse(health, status=status)
386
-
387
- # urls.py
388
- urlpatterns = [
389
- path('health/tasks/', health_check, name='tasks_health'),
390
- ]
391
- ```
392
-
393
- ---
394
-
395
- ## 🔧 Advanced Configuration
396
-
397
- ### Custom Middleware
398
-
399
- ```python
400
- # custom_middleware.py
401
- import dramatiq
402
- from dramatiq.middleware import Middleware
403
-
404
- class CustomLoggingMiddleware(Middleware):
405
- def before_process_message(self, broker, message):
406
- logger.info(f"Processing task: {message.actor_name}")
407
-
408
- def after_process_message(self, broker, message, *, result=None, exception=None):
409
- if exception:
410
- logger.error(f"Task failed: {message.actor_name} - {exception}")
411
- else:
412
- logger.info(f"Task completed: {message.actor_name}")
413
-
414
- # Add to middleware list
415
- middleware = [
416
- "dramatiq.middleware.AgeLimit",
417
- "myapp.middleware.CustomLoggingMiddleware", # Custom middleware
418
- "dramatiq.middleware.TimeLimit",
419
- "dramatiq.middleware.Callbacks",
420
- "dramatiq.middleware.Retries",
421
- "dramatiq.middleware.Shutdown",
422
- ]
423
- ```
424
-
425
- ### Queue Routing
426
-
427
- ```python
428
- # Route different tasks to different queues
429
- @dramatiq.actor(queue_name="high")
430
- def urgent_task():
431
- pass
432
-
433
- @dramatiq.actor(queue_name="knowledge")
434
- def process_document():
435
- pass
436
-
437
- @dramatiq.actor(queue_name="low")
438
- def cleanup_task():
439
- pass
440
- ```
441
-
442
- ### Rate Limiting
443
-
444
- ```python
445
- # Add rate limiting middleware
446
- from dramatiq.rate_limits import ConcurrentRateLimiter
447
- from dramatiq.rate_limits.backends import RedisBackend
448
-
449
- # Configure rate limiter
450
- backend = RedisBackend(url="redis://localhost:6379/2")
451
- rate_limiter = ConcurrentRateLimiter(backend, "api-calls", limit=10)
452
-
453
- @dramatiq.actor
454
- def api_call_task():
455
- with rate_limiter.acquire():
456
- # Make API call
457
- pass
458
- ```
459
-
460
- ---
461
-
462
- ## 🧪 Testing Configuration
463
-
464
- ### Test Settings
465
-
466
- ```python
467
- # settings/test.py
468
- import tempfile
469
-
470
- # Use in-memory Redis for tests
471
- DRAMATIQ_BROKER = {
472
- "BROKER": "dramatiq.brokers.stub.StubBroker",
473
- "OPTIONS": {},
474
- "MIDDLEWARE": [
475
- "dramatiq.middleware.AgeLimit",
476
- "dramatiq.middleware.TimeLimit",
477
- "dramatiq.middleware.Callbacks",
478
- "dramatiq.middleware.Retries",
479
- ],
480
- }
481
-
482
- # Or use separate Redis DB
483
- DRAMATIQ_BROKER = {
484
- "BROKER": "dramatiq.brokers.redis.RedisBroker",
485
- "OPTIONS": {
486
- "url": "redis://localhost:6379/15", # Test DB
487
- },
488
- }
489
- ```
490
-
491
- ### Test Utilities
492
-
493
- ```python
494
- # test_utils.py
495
- import dramatiq
496
- from dramatiq.brokers.stub import StubBroker
497
-
498
- def setup_test_broker():
499
- """Set up test broker for unit tests."""
500
- broker = StubBroker()
501
- dramatiq.set_broker(broker)
502
- return broker
503
-
504
- def clear_test_queues():
505
- """Clear all test queues."""
506
- broker = dramatiq.get_broker()
507
- if hasattr(broker, 'flush_all'):
508
- broker.flush_all()
509
- ```
510
-
511
- ---
512
-
513
- ## 📝 Configuration Validation
514
-
515
- ### Validation Rules
516
-
517
- ```python
518
- # Built-in validation in TaskConfig
519
- @field_validator("enabled")
520
- @classmethod
521
- def validate_enabled_with_environment(cls, v: bool) -> bool:
522
- """Validate task system can be enabled in current environment."""
523
- if v:
524
- try:
525
- import dramatiq
526
- import redis
527
- except ImportError as e:
528
- raise ValueError(f"Missing required dependency: {e}")
529
- return v
530
-
531
- @field_validator("dramatiq")
532
- @classmethod
533
- def validate_dramatiq_config(cls, v: DramatiqConfig) -> DramatiqConfig:
534
- """Validate Dramatiq configuration."""
535
- if v.processes < 1:
536
- raise ValueError("processes must be >= 1")
537
- if v.threads < 1:
538
- raise ValueError("threads must be >= 1")
539
- if v.redis_db < 0 or v.redis_db > 15:
540
- raise ValueError("redis_db must be 0-15")
541
- return v
542
- ```
543
-
544
- ### Configuration Check Command
545
-
546
- ```python
547
- # management/commands/check_tasks.py
548
- from django.core.management.base import BaseCommand
549
- from django_cfg.modules.django_tasks import get_task_service
550
-
551
- class Command(BaseCommand):
552
- help = 'Check task system configuration'
553
-
554
- def handle(self, *args, **options):
555
- service = get_task_service()
556
-
557
- if not service.is_enabled():
558
- self.stdout.write(
559
- self.style.WARNING('Task system is disabled')
560
- )
561
- return
562
-
563
- # Check configuration
564
- if service.validate_configuration():
565
- self.stdout.write(
566
- self.style.SUCCESS('✅ Task system configuration is valid')
567
- )
568
- else:
569
- self.stdout.write(
570
- self.style.ERROR('❌ Task system configuration is invalid')
571
- )
572
-
573
- # Check Redis connection
574
- if service.check_redis_connection():
575
- self.stdout.write(
576
- self.style.SUCCESS('✅ Redis connection is working')
577
- )
578
- else:
579
- self.stdout.write(
580
- self.style.ERROR('❌ Redis connection failed')
581
- )
582
-
583
- # Show discovered tasks
584
- discovered = service.discover_tasks()
585
- self.stdout.write(f"Discovered task modules: {discovered}")
586
- ```
587
-
588
- **Usage**: `python manage.py check_tasks`
589
-
590
- ---
591
-
592
- ## 🚨 Common Configuration Mistakes %%PRIORITY:HIGH%%
593
-
594
- ### ❌ Wrong Redis Database
595
-
596
- ```python
597
- # DON'T: Use same DB as cache
598
- CACHES = {'default': {'LOCATION': 'redis://localhost:6379/2'}}
599
- DRAMATIQ_REDIS_DB = 2 # Conflict!
600
-
601
- # DO: Use separate databases
602
- CACHES = {'default': {'LOCATION': 'redis://localhost:6379/0'}}
603
- DRAMATIQ_REDIS_DB = 2 # Separate DB
604
- ```
605
-
606
- ### ❌ Incorrect Middleware Order
607
-
608
- ```python
609
- # DON'T: Wrong order
610
- middleware = [
611
- "dramatiq.middleware.Retries", # Should be later
612
- "dramatiq.middleware.AgeLimit", # Should be first
613
- ]
614
-
615
- # DO: Correct order
616
- middleware = [
617
- "dramatiq.middleware.AgeLimit", # Age limit first
618
- "dramatiq.middleware.TimeLimit", # Time limit second
619
- "dramatiq.middleware.Callbacks", # Callbacks third
620
- "dramatiq.middleware.Retries", # Retries fourth
621
- "dramatiq.middleware.Shutdown", # Shutdown last
622
- ]
623
- ```
624
-
625
- ### ❌ Missing Environment Variables
626
-
627
- ```python
628
- # DON'T: Hardcode settings
629
- DRAMATIQ_BROKER = {
630
- "OPTIONS": {"url": "redis://localhost:6379/2"}
631
- }
632
-
633
- # DO: Use environment variables
634
- import os
635
- DRAMATIQ_BROKER = {
636
- "OPTIONS": {
637
- "url": os.getenv('REDIS_URL', 'redis://localhost:6379/2')
638
- }
639
- }
640
- ```
641
-
642
- ---
643
-
644
- ## 🧠 Best Practices
645
-
646
- ### Configuration Management
647
-
648
- 1. **Environment-Specific**: Use different configs for dev/staging/prod
649
- 2. **Validation**: Always validate configuration on startup
650
- 3. **Secrets**: Use environment variables for sensitive data
651
- 4. **Documentation**: Document all configuration options
652
- 5. **Defaults**: Provide sensible defaults for all settings
653
-
654
- ### Resource Planning
655
-
656
- 1. **Memory**: Plan for ~50MB per worker process
657
- 2. **CPU**: 1 process per CPU core is good starting point
658
- 3. **Redis**: Monitor memory usage, enable persistence
659
- 4. **Network**: Consider Redis connection limits
660
- 5. **Disk**: Plan for log storage and Redis persistence
661
-
662
- **DEPENDS_ON**: Django-CFG, Dramatiq, Redis
663
- **USED_BY**: Development, DevOps, System administrators